我在使用由 Unsafe.defineAnonymousClass()
加载的生成的字节码类时遇到困难。我想知道如何使用匿名类的对象来初始化另一个类(或匿名类)。
以下面的示例类 Callee 为例,其构造函数接受 Callee2 作为参数。
Class Callee{
Callee2 _call2;
public Callee(Callee2 callee2){
...
}
}
在运行时,我为 Callee2 和 Callee 生成了新类,并且这两个新类都由 Unsafe.defineAnonymousClass()
加载。对于new Callee,构造函数也改为:
public test.code.jit.asm.simple.Callee(test.code.jit.asm.simple.Callee2.1506553666);
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=2
0: aload_0
1: invokespecial #65 // Method java/lang/Object."<init>":()V
....
8: return
而Callee2生成的类名是:
class test.code.jit.asm.simple.Callee2/1506553666
我创建了一个 `Callee2/1506553666' 实例并想用它创建新的 Callee 实例,但是失败了:
newCls.getConstructor(args).newInstance(objs);
在哪里
args = [class test.code.jit.asm.simple.Callee2/1506553666]
和
objs= [test.code.jit.asm.simple.Callee2/1506553666@39b0a038]
args[0] 没有意义,因为这个类是由匿名加载器加载的(匿名类不被任何类加载器引用)。所以我真的很困惑如何将 objs 数组中的对象传递给方法调用..
调用 getConstructor (args) 时发生异常,消息为:
java.lang.NoClassDefFoundError: test/code/jit/asm/simple/Callee2/1506553666
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2483)
at java.lang.Class.getConstructor0(Class.java:2793)
at java.lang.Class.getConstructor(Class.java:1708)
at code.jit.asm.util.ReflectionUtil.adapt2GeneratedObject(ReflectionUtil.java:36)
at code.jit.asm.services.BytecodeGenerator.generator(BytecodeGenerator.java:164)
这个异常(exception)对我来说很明显,因为匿名类对于任何类加载器都是 transient 的。但就我而言,我确实需要通过新的 Callee2 实例初始化新的 Callee(也是匿名类)(Callee 的构造函数中的字节码将读取 Callee2 的字段成员),那么是否有任何解决方法可以为新的被调用者的构造函数传递新的 callee2 实例?
最佳答案
看看the signature and documentation comment ,在标准 API 文档中不可用,因为它不是官方 API 的一部分:
Define a class but do not make it known to the class loader or system dictionary. For each CP entry, the corresponding CP patch must either be null or have the a format that matches its tag:
- Integer, Long, Float, Double: the corresponding wrapper object type from java.lang
- Utf8: a string (must have suitable syntax if used as signature or name) Class: any java.lang.Class object
- String: any object (not just a java.lang.String)
- InterfaceMethodRef: (NYI) a method handle to invoke on that call site's arguments
… (params:)
cpPatches
where non-null entries exist, they replace corresponding CP entries in datapublic native Class<?> defineAnonymousClass( Class<?> hostClass, byte[] data, Object[] cpPatches);
换句话说,您可以提供一个与您要定义的类的常量池大小相同的数组。将 null
保留在您不想修改的地方。就在您的常量池有代表匿名类的 CONSTANT_Class_info
的地方,您只需传递数组中关联的 Class
对象。因此无需查找类,您甚至不必在类字节中提供正确的类名。
有一些明显的限制:
- 如果你有循环依赖,就会出现问题,因为你需要一个已经存在的
Class
对象来修补另一个类的池。好吧,对于已知会延迟解析的类用途,它可能会起作用 - 您只能将
CONSTANT_Class_info
修补到一个足够的Class
,例如访问该类的成员或创建它的新实例。但当匿名类是签名的一部分时,它没有帮助,即您想要声明该类型的字段或使用将其作为参数或返回类型的方法。但是您可以使用通过MethodHandle
修补CONSTANT_InterfaceMethodref_info
条目的选项来访问此类方法(咳咳,一旦实现,我猜“NYI”的意思是“尚未实现”)......
关于java - 如何在另一个生成字节码类中使用匿名类实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31349729/