我正在使用BCEL对方法字节码进行转换,实现匿名内部类风格的方法拦截器,在拦截方法的同时,需要对拦截的方法进行一些注解处理。我使用 BCEL 来拦截除 java 反射之外的方法访问。
现在我的代码可以很好地处理没有原始类型的方法。由于我不知道如何将 Class.getDeclaredMethod 与原始参数类型列表一起使用,因为 getDeclaredMethod 接受 methodName 和 Class[] 数组作为参数。
所以第一个问题是如何做到这一点。
然后我发现在JDK7中,我可以直接通过CONSTANT_MethodHandle在java类文件中用ldc_w字节码获取一个MethodHandle引用。就像用ldc引用Java类一样,如果我能直接用ldc_w引用java.lang.reflection.Method,那我就省下了做反射的时间,就不会被第一个问题上面提到的原始类型所困扰.我试过了,但没能做到。
那么第二个问题是我可以使用 ldc_w 来引用 java.lang.reflection.Method 吗?
第三个问题是我可以将 MethodHandle 转换为 java.lang.reflection.Method 或相应方法上的 Annotations 吗?
谢谢 Holger,您的回答让我几乎完全清楚,但还有以下问题。我可能误解了你的回答,现在我在运行时遇到异常:
Exception in thread "main" java.lang.NoClassDefFoundError: long
at net.madz.lifecycle.demo.standalone.ServiceOrder.allocateResources(ServiceOrder.java)
at net.madz.lifecycle.demo.standalone.Main2.main(Main2.java:18)
Caused by: java.lang.ClassNotFoundException: long
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 2 more
代码如下//2.5 为数组中的每个元素赋值
// Step 2. final InterceptContext<Void> context = new
// InterceptContext<Void>(getClass(), this, "allocateResources",
// new Class[] { Long.class, Long.class, Long.class });
// 2.1 getClass()
ilist.append(ifact.createNew(InterceptContext.class.getName()));
ilist.append(InstructionFactory.DUP);
ilist.append(new LDC(cgen.getConstantPool().lookupClass(interceptingClass)));
// 2.2 load this
ilist.append(InstructionFactory.createLoad(new ObjectType(interceptingClass), 0));// this
// 2.3 load intercepting method
int methodNameIndex = cgen.getConstantPool().lookupString(interceptingMethod);
if ( -1 >= methodNameIndex ) {
methodNameIndex = cgen.getConstantPool().addString(interceptingMethod);
}
ilist.append(new LDC(methodNameIndex));// methodName
// 2.4 calculate argument size and allocate an array with same size
ilist.append(new ICONST(types.length));
ilist.append(ifact.createNewArray(new ObjectType("java.lang.Class"), (short) 1));
// 2.5 assign value for each element in array
for ( int i = 0; i < types.length; i++ ) {
ilist.append(InstructionFactory.DUP);
ilist.append(new ICONST(i));
String className = convertType2ClassName(types[i]);
int argumentClassIndex = cgen.getConstantPool().lookupClass(className); // ?
if ( -1 >= argumentClassIndex ) {
argumentClassIndex = cgen.getConstantPool().addClass(className);
}
if ( types[i].getSize() > 4 ) {
ilist.append(new LDC_W(argumentClassIndex));
} else {
ilist.append(new LDC(argumentClassIndex));
}
ilist.append(InstructionConstants.AASTORE);
}
// 2.6 new InterceptContext<Void>(...
final Type[] interceptor_method_arg_types = new Type[4];
interceptor_method_arg_types[0] = new ObjectType("java.lang.Class");
interceptor_method_arg_types[1] = new ObjectType("java.lang.Object");
interceptor_method_arg_types[2] = new ObjectType("java.lang.String");
interceptor_method_arg_types[3] = new ArrayType("java.lang.Class", 1);
ilist.append(ifact.createInvoke(InterceptContext.class.getName(), "<init>", Type.VOID,
interceptor_method_arg_types, Constants.INVOKESPECIAL));
convertType2ClassName 如下:
private static String convertType2ClassName(Type type) {
if ( Type.BOOLEAN.equals(type) ) {
return boolean.class.getName();
} else if ( Type.BYTE.equals(type) ) {
return byte.class.getName();
} else if ( Type.CHAR.equals(type) ) {
return char.class.getName();
} else if ( Type.DOUBLE.equals(type) ) {
return double.class.getName();
} else if ( Type.FLOAT.equals(type) ) {
return float.class.getName();
} else if ( Type.INT.equals(type) ) {
return int.class.getName();
} else if ( Type.LONG.equals(type) ) {
return long.class.getName();
} else if ( Type.SHORT.equals(type) ) {
return short.class.getName();
} else if ( type instanceof ObjectType ) {
String signature = type.getSignature();
if ( signature.startsWith("L") ) {
signature = signature.substring(1);
}
int leftArrow = signature.indexOf("<");
if ( -1 < leftArrow ) {
signature = signature.substring(0, leftArrow);
}
if ( signature.endsWith(";") ) {
signature = signature.substring(0, signature.length() - 1);
}
return signature;
} else if ( type instanceof ArrayType ) {
//unsupport for now
}
//wrong return
return type.getSignature();
}
最佳答案
Holger's answer 涵盖了您的第一个问题(带有原始参数的 getDeclaredMethod)。您的第二个问题(使用 ldc
获取 java.lang.reflect.Method)的答案仍然是否。但是你的第三个问题的答案(将方法句柄转换为 java.lang.reflect.Method)随着 Java 8 的发布而改变了——答案现在是有时,包括你关心的情况(转换一个句柄你只是 ldc
'd)。
从 Java 8 开始,可以“破解”直接方法句柄 以获取 Method、Constructor 或 Field 对象。直接方法将 are defined 处理为由 CONSTANT_MethodHandle 常量的 ldc
或 MethodHandles.Lookup 中的 find*
和 unreflect*
方法生成的句柄,没有进一步的转换(没有绑定(bind)参数等)。破解直接方法句柄有两种途径:
- 调用 MethodHandles.reflectAs ,传递预期的类(方法、构造函数或字段)和要破解的句柄。此方法需要
ReflectPermission("suppressAccessChecks")
,因此如果您需要在安全管理器下运行,则可能不合适。 - 获得一个 MethodHandles.Lookup“等同于创建目标方法句柄的方法,或者具有足够的访问权限来重新创建等效方法句柄”(根据 MethodHandleInfo 文档),然后调用 Lookup.revealDirect 传递句柄以破解以获得MethodHandleInfo,然后调用 MethodHandleInfo.reflectAs 传递预期的类(方法、构造函数或字段)和查找。为了维护安全性,此路径通过检查查找类来特殊处理 caller-sensitive methods。
第一种途径更方便,但如果您需要在安全管理器下运行,则需要在每个需要破解方法句柄的类中创建一个 Lookup(存储在 <clinit>
中初始化的新静态字段中)并使用该查找来破解句柄。
关于java - 是否可以直接从java类文件的Constant_Method_REF获取java.lang.reflection.Method?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19548573/