在上一篇Proxy源码分析中,我们讲到关于invoke方法的回调。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(obj, args);
return null;
}
今天我们来看一下Method类和它的invoke方法的实现。
public final class Method extends AbstractMethod implements GenericDeclaration, Member {
...
}
final 修饰
AbstractMethod 抽象类 这个类代表一个抽象方法
GenericDeclaration 泛型声明 TypeVariable<?>[] getTypeParameters();
Member 指定所有公共类或接口的成员(包括继承的成员)。
可见Method的主要作用:Method类和它从member类继承而来的方法使得我们可以获得方法声明的完整信息.
主要的一个方法。
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException {
/*检查 AccessibleObject的override属性是否为true(override属性默认为false)
* AccessibleObject是Method的父类
* (Method extends Executable, Executable extends AccessibleObject),
* 可调用setAccessible方法改变(setAccessible是AccessibleObject中的方法)
* 如果override设置为true,则表示可以忽略访问权限的限制,直接调用。
*/
if (!override) {
//检查方法是否为public的
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
//获得到调用这个方法的Class
Class<?> caller = Reflection.getCallerClass();
/*校验是否有访问权限,这个方法在AccessibleObject中,
*有缓存机制,如果下次还是这个类来调用就不用去做校验了,直接用上次的结果
*但是如果下次换一个类来调用,就会重新校验,
*因此这个缓存机制只适用于一个类的调用
*/
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor;// 两个版本,一个是Java实现的,另一个是native code实现的。
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);//返回MethodAccessor.invoke
}
调用方式:
Class<?> sClazz = Class.forName(“Test”);
Object sObj = sClazz.newInstance();
Method method1 = sClazz.getDeclaredMethod("getParamsLength", String.class, String.class);
method1.invoke(sObj, "Hello", "Java");
//也可以写成这样
Class<?>[] clazzs = {String.class, String.class};
Method method2 = sClazz.getDeclaredMethod("getParamsLength", clazzs);
method2.invoke(sObj, "Hello", "Java");
调用的时候,参数类型必须一一对应。含有泛型的参数,会被擦除掉泛型。
需要注意的是:静态方法直接调用,不用invoke那样用。
Method对象最有趣的用法就是反射地调用它自己?
当方法设置的参数是接口的时候的一种实现。?
使用场景:
当我们可以使用这种形式的调用时,我们有充分的理由去避免它。但是如果我们在编写调试器或其他需要将用户输入解释为对对象操作的泛型应用时使用invoke或get/set方法,就会显得很合理.
另外:
在对象转换为json字符串时,也使用,就是在对象序列化的时候,不需要关心方法的名称是什么。直接遍历对象的所有方法去进行操作。
getAnnotation() 注解
getModifiers() 修饰符
isVarArgs 可变参数
isSynthetic() 合成
getDeclaringClass() 声明
getExceptionTypes()
getParameterTypes()
getReturnType()
getTypeParameters()
getGenericParameterTypes() 通用的
invoke() 调用
toString() 拼接返回类型、声明的Class的名称、方法名、参数类型、和异常信息。
实际的MethodAccessor只是一个方法的接口,其invoke()方法与Method.invoke()的对应。创建MethodAccessor实例的是ReflectionFactory。实际的MethodAccessor实现有两个版本,一个是Java实现的,另一个是native code实现的。Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。这是HotSpot(jvm的动态编译)的优化方式带来的性能特性,同时也是许多虚拟机的共同点。为了权衡两个版本的性能,Sun的JDK使用了“inflation”的技巧:让Java方法在被反射调用时,开头若干次使用native版,等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版。
Sun的JDK是从1.4系开始采用这种优化的。
备注:HotSpot对bytecode的编译不是在程序运行前编译的,而是在程序运行过程中编译的。
以下是为了解释Method类中泛型擦除:
Field类中定义了一些方法,可以用来查询字段的类型以及设置或读取字段的值。将这些方法与继承而来的member方法结合在一起.就可以使我们能够找出有关字段声明的全部信息,并且能够操纵某个特定对象或类的字段。
getGenericType方法返回表示字段的声明类型的Type实例。对于像String或int这样的平凡类型,该方法将返回与其相关联的Class对象,例如String.class和int.classo对于像List < Stri ng>这样的参数化类型,该方法将返回Parameterizedrype的实例,例如,对像T这样的类型,该方法将返回Typevariable实例。
遗留下来的getType方法将返回字段的类型的Class对象。对于平凡类型,该方法的行为与getGenericType方法的相同。如果字段的声明类型是参数化类型,那么getType方法将返回参数化类型的擦除所对应的Class对象,即原始类型的Class对象。例如,对于声明为List < Stri ng>的对象,getType将返回Li St. class的。如果字段的声明类型是类型变量,那么getType方法将返回类型变量的擦除所对应的class对象。例如,假设有一个类FOO<T>,对于其声明为T类型的字段,getType将返回object.
class对象。如果FOO被声明为FOO<T extends Number >,那么getType将返回 Number.class.