我使用BCEL转换方法字节代码来实现具有匿名内部类样式的方法拦截器,同时拦截该方法,我需要在截获的方法上处理一些注释。我使用BCEL来拦截除java反射之外的方法访问。
现在我的代码可以很好地处理没有原始类型的方法。由于我不知道如何使用Class.getDeclaredMethod和原始参数类型列表,因为getDeclaredMethod接受methodName和Class []数组作为参数。
所以第一个问题是如何做到这一点。
然后我在JDK7中发现,我可以直接通过CONSTANT_MethodHandle获取一个MethodHandle引用,并在java类文件中使用ldc_w字节代码。就像使用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();
}
答案 0 :(得分:4)
Holger's answer涵盖了您的第一个问题(带有原始参数的getDeclaredMethod)。第二个问题(使用ldc
获取java.lang.reflect.Method)的答案仍然是 no 。但是你的第三个问题(将方法句柄转换为java.lang.reflect.Method)的答案随着Java 8的发布而改变了 - 答案现在是有时,包括你的情况关心(只转换句柄ldc
&#39; d)。
从Java 8开始,可能会破解&#34; 直接方法处理以获取Method,Constructor或Field对象。 Direct方法处理are defined作为CONSTANT_MethodHandle常量的ldc
或MethodHandles.Lookup中的find*
和unreflect*
方法生成的句柄,没有进一步的转换(没有绑定参数等) )。破解直接方法句柄有两种方法:
ReflectPermission("suppressAccessChecks")
,因此如果您需要在安全管理器下运行,则可能不合适。第一个路径更方便,但是如果你需要在安全管理器下运行,你需要在每个类中创建一个Lookup来破解方法句柄(存储在一个初始化的新静态字段中) <clinit>
)并使用该查找来处理句柄。
答案 1 :(得分:1)
您无法获得java.lang.reflection.Method
ldc
。对ldc
执行CONSTANT_MethodHandle
会产生MethodHandle
。
这些方法句柄可用于执行相关代码(尽管它的名称,不限于方法),您可以查询其参数类型。但是你无法将其转换为反射Method
。虽然您可以执行相反操作,但请将java.lang.reflection.Method
转换为java.lang.invoke.MethodHandle
。
由于java.lang.invoke.MethodHandle
可以更快地调用方法,而且没有自动装箱的开销,也没有将所有参数放入数组中,所以无论如何你可能不会使用它。
顺便说一句,使用基本类型的反射非常容易,例如。
obj.getClass()
.getDeclareMethodMethod("foo", int.class, String.class)
.invoke(obj, 42, "blah");
它会将int
包装到Integer
中,并创建一个包含两个参数Object[]
的临时Object
数组。但是对于方法的查找,您必须指定正确的基元类型。
从Java 8开始,有a way to convert a direct MethodHandle
to a Method
,因此您可以使用ldc
指令然后进行转换,但是您需要获取MethodHandles.Lookup
实例并使用它两次,所以字节代码看起来像这样:
ldc class java/lang/reflect/Method
ldc method handle <your desired method>
invokestatic java/lang/invoke/MethodHandles.reflectAs:(Ljava/lang/Class;Ljava/lang/invoke/MethodHandle;)Ljava/lang/reflect/Member;
checkcast java/lang/reflect/Method
略短于,例如
ldc class <declaring class of your desired method>
ldc string <name of your desired method>
ldc method type <type signature of your desired method>
invokevirtual java/lang/invoke/MethodType.parameterArray:()[Ljava/lang/Class;
invokevirtual java/lang/Class.getDeclaredMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
请记住,在字节码级别上,没有强制执行异常处理