相关类介绍
MethodType类
MethodType
是不可变类,提供了对方法所有输入参数和输出参数的描述。创建MethodType
类实例的方法是调用静态方法MethodType.methodType()
,该方法的第一个参数是方法的返回类型,之后的所有参数都是方法的参数类型。MethodType
类通过静态方法MethodType.methodType
创建。
// 创建一个MethodType类型对象,其返回值是String,第一个参数类型是char,第二个参数类型是char
mt = MethodType.methodType(String.class, char.class, char.class);
MethodHandle类
MethodHandle
类提供了对类方法的反射调用。常用的方法有invoke
和invokeExact
。invoke
方法检查对象的相应方法时如果发现参数类型/返回类型不匹配会首先调用MethodHandle
的asType
方法将原来的参数类型转换为方法要求的参数类型,之后在接着调用方法。
invoke
和invokeExact
方法的参数都是可变参数,当MethodHandle
实例表示的是静态方法时,传入的参数都是静态方法的参数;当MethodHandle
实例表示的是实例方法时,传入的参数第一个是实例,之后的才是方法参数。
MethodHandles类
MethodHandles
类提供了一系列静态方法生成MethodHandle
类。该类提供的静态方法可以归结微以下三类:
- 查找方法,帮助创建方法和属性的
MethodHandle
类;- 组合器方法,用于将现有的
MethodHandle
实例转换成新的实例;- 用于创建模拟其他
JVM
操作的MethodHandler
实例的工厂方法。
Methodhandles.Lookup类
该类的主要作用是创建MethodHandle
的工厂类。该类在创建MethodHandle
实例时会检查调用者是否对反射的方法有相应的调用权限。创建Lookup
对象的方法有以下两种:
MethodHandles.lookup()
创建一个Lookup
对象,该对象可以创建所有访问权限的方法,包括public
,protected
,private
,default
。MethodHandles.publicLookup()
创建一个Lookup
对象,该对象只能创建具有public
访问权限的方法。
举例
示例一
Object x, y; String s; int i;
MethodType mt; MethodHandle mh;
MethodHandles.Lookup lookup = MethodHandles.lookup();
// mt is (char,char)String
mt = MethodType.methodType(String.class, char.class, char.class);
mh = lookup.findVirtual(String.class, "replace", mt);
s = (String) mh.invokeExact("daddy",'d','n');
// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
assertEquals(s, "nanny");
// weakly typed invocation (using MHs.invoke)
s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
assertEquals(s, "savvy");
// mt is (Object[])List
mt = MethodType.methodType(java.util.List.class, Object[].class);
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
assert(mh.isVarargsCollector());
x = mh.invoke("one", "two");
// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
assertEquals(x, java.util.Arrays.asList("one","two"));
// mt is (Object,Object,Object)Object
mt = MethodType.genericMethodType(3);
mh = mh.asType(mt);
x = mh.invokeExact((Object)1, (Object)2, (Object)3);
// invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
assertEquals(x, java.util.Arrays.asList(1,2,3));
// mt is ()int
mt = MethodType.methodType(int.class);
mh = lookup.findVirtual(java.util.List.class, "size", mt);
i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
// invokeExact(Ljava/util/List;)I
assert(i == 3);
mt = MethodType.methodType(void.class, String.class);
mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
mh.invokeExact(System.out, "Hello, world.");
// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
示例二(来自Mybatis的示例)
static {
Method privateLookupIn;
try {
privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
} catch (NoSuchMethodException e) {
privateLookupIn = null;
}
privateLookupInMethod = privateLookupIn;
Constructor<Lookup> lookup = null;
if (privateLookupInMethod == null) {
// JDK 1.8
try {
lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
lookup.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
e);
} catch (Exception e) {
lookup = null;
}
}
lookupConstructor = lookup;
}
以上代码首先反射获取MethodHandle.privateLookupIn
方法,如果没有该方法的话说明运行的jre
版本在1.8以下,因此反射获取Lookup
对象的构造方法Lookup(Class, int)
,该构造方法的源码为:
private Lookup(Class<?> lookupClass, int allowedModes) {
this.lookupClass = lookupClass;
this.allowedModes = allowedModes;
}
第一个参数为查找方法的类,第二个参数为允许的模式。
在MapperProxy
接下来的代码中,我们发现方法getMethodHandleJava8
使用属性lookupConstructor
创建了参数method
的MethodHadle
对象。而传递给newInstance
方法的参数ALLOWED_MODES
即为允许的模式,该属性为MapperProxy
的静态属性。
private MethodHandle getMethodHandleJava8(Method method)
throws IllegalAccessException, InstantiationException, InvocationTargetException {
final Class<?> declaringClass = method.getDeclaringClass();
// 方法unreflectSpecial为反射的方法 method 创建对应的MethodHandle对象
return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass);
}
private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
可以看到ALLOWE_MODES
为MethodHandles
中定义的四种方位权限的或,因此lookup
对象的访问权限为所有的方法。