2016-11-23 106 views
4

我有一个类如何在运行时创建一个Java类的副本?

class Foo { 
    int increment(int x) { 
     return x + 1; 
    } 
} 

我要获得这个类的副本在运行时,E。 G。类似

class Foo$Copy1 { 
    int increment(int x) { 
     return x + 1; 
    } 
} 

它具有所有相同的方法,但名称不同。

Proxy似乎帮助委托,但不复制他们的所有机构的方法。

+2

您为什么要这么做?方法不会被实例化。你确定你在谈论一个班级而不是一个对象吗? –

+3

显而易见的问题是:你为什么要这样做?您不能将通过标准类加载器加载的类重新转换为其来自的类文件,您可以编写自己的类加载器来拦截对“defineClass()”的调用。 – biziclop

+0

@biziclop我想保持复制方法中每个方法调用的简介清晰单形。 – leventov

回答

8

您可以使用Byte Buddy此:

Class<?> type = new ByteBuddy() 
    .redefine(Foo.class) 
    .name("Foo$Copy1") 
    .make() 
    .load(Foo.class.getClassLoader()) 
    .getLoaded(); 

Method method = type.getDeclaredMethod("increment", int.class); 
int result = (Integer) method.invoke(type.newInstance(), 1); 

注意,这种方法重新定义内Foo,例如类的任何用途如果返回Foo的方法,它现在会返回Foo$Copy1。所有代码参考都一样。

+0

谢谢。它是否使用私有的ClassLoader的'defineClass()'方法或者换个角度? – leventov

+0

您可以选择一个'ClassLoadingStrategy'作为第二个参数。默认情况下,除非提供了引导类加载器,否则使用注入'Foo'的类加载器。使用'ClassLoadingStrategy.Default.WRAPPER',你也可以创建一个不需要的类加载器。 –

0

我认为使用不安全可能是足够的,如果你有正常的字节码访问。

类似Foo.class.getClassLoader().getResourceAsStream()可以给你类的字节码。

然后使用 sun.misc.Unsafe.defineClass(String name, byte[] code, int off, int len, ClassLoader classLoader, ProtectionDomain protectionDomain)在与Foo相同的类加载器和保护域中定义类,但使用不同的名称。

细节是要弄清楚的,但它可能是没有任何第三方库的最简单的方法。

+0

可能ByteBuddy方法更灵活,更容易维护。 –

+2

尽管如此,您仍然需要重新定义类文件中的类名称以及任何自引用。只使用'Foo'命名的类文件是行不通的。 –

2
+0

对于那些已经使用Javassist的人来说,这可能是最合适的解决方案。此外,我也没有偏好Javassist和Byte Buddy – leventov

+0

@leventov另外,您也可以使用JDK的BCEL(它来自JDK,但不是公共API,因为它位于“com.sun.org.apache.bcel.internal “包)而不依赖于Javassist。 – sozal

+0

like this' String copyClassName = classToBeCopied.getName()+“$ Copy”; ClassGen classGen = new ClassGen(Repository.lookupClass(classToBeCopied)); classGen.setClassName(copyClassName); byte [] bytecode = classGen.getJavaClass()。getBytes(); 返回UNSAFE。的defineClass( copyClassName, 字节码, 0, bytecode.length, classToBeCopied.getClassLoader(), classToBeCopied.getProtectionDomain()); ' – sozal

相关问题