目录
JAVA中invoke是来自于method类的invoke()方法,他可以完成动态调用
Object | invoke(Object obj, Object... args) 调用底层的方法,这 |
invoke方法的参数,一个是Object类型,也就是调用该方法的对象。
第二个参数是一个可变参数类型,这个可变参数类型是如何能传递一个数组类型,一个为多个参数,另一个为一个数组参数,很明显参数的个数不匹配,我们这时候要将可变参数变成一个参数,
解决方案:
1.将传递进入的参数强转为Object类型
2.将参数重新包装成一个Object数组
public class Aa {
public void test(String[] arg){
for (String string : arg) {
System.out.println(string);
}
}
@Test
public void demo1() throws Exception {
//获取字节码对象
Class<Aa> clazz = (Class<Aa>) Class.forName("online.Aa");
//获取一个对象
Constructor con = clazz.getConstructor();
Aa a = (Aa) con.newInstance();
String[] s = new String[]{"aa","bb"};
//获取Method对象
Method method = clazz.getMethod("test", String[].class);
//调用invoke方法来调用
//重新包装成一个Obiect数组
method.invoke(a, new Object[]{s});
//强转为Object类型
method.invoke(a,(Object)s);
}
}
当我们将鼠标放到invoke上时,ctrl+鼠标左键进入
@CallerSensitive public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Reflection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers); } } MethodAccessor ma = methodAccessor; // read volatile if (ma == null) { ma = acquireMethodAccessor(); } return ma.invoke(obj, args); }
invoke()对带有指定参数的指定对象调用,个别参数被自动解包,与基本形参相匹配,基本参数与引用参数都需服从方法调用转换.
参数obj 表示的是基本的方法被调用的对象 args 用于方法的调用
return返回的结果表示的对象和参数 args
obj
调度方法的结果
解析源码
1.先检查 AccessibleObject的override属性是否为true。
AccessibleObject是Method,Field,Constructor的父类,override属性默认为false,可调用setAccessible方法改变,如果设置为true,则表示可以忽略访问权限的限制,直接调用。
2.如果不是ture,则要进行访问权限检测。用Reflection的quickCheckMemberAccess方法先检查是不是public的,如果不是再用Reflection.getCallerClass(1)方法获
得到调用这个方法的Class,然后做是否有权限访问的校验,校验之后缓存一次,以便下次如果还是这个类来调用就不用去做校验了,直接用上次的 结果,(很奇怪用这种方式缓存,因为这种方式如果下次换个类来调用的话,就不用会缓存了,而再验证一遍,把这次的结果做为缓存,但上一次的缓存结果就被冲 掉了。这是一个很简单的缓冲机制,只适用于一个类的重复调用)。
3.调用MethodAccessor的invoke方法。每个Method对象包含一个root对象,root对象里持有一个 MethodAccessor对象。我们获得的Method独享相当于一个root对象的镜像,所有这类Method共享root里的 MethodAccessor对象,(这个对象由ReflectionFactory方法生成,ReflectionFactory对象在Method类 中是static final的由native方法实例化)。
ReflectionFactory生成MethodAccessor:如果noInflation的属性为true则直接返回MethodAccessorGenerator创建的一个MethodAccessor。
否则返回DelegatingMethodAccessorImpl,并将他与一个NativeMethodAccessorImpl互相引 用。但DelegatingMethodAccessorImpl执行invoke方法的时候又委托给NativeMethodAccessorImpl 了。
再一步深入
4.NativeMethodAccessorImpl的invkoe方法:
调用natiave方法invoke0执行方法调用.
注意这里有一个计数器numInvocations,每调用一次方法+1,当 比 ReflectionFactory.inflationThreshold(15)大的时候,用MethodAccessorGenerator创 建一个MethodAccessor,并把之前的DelegatingMethodAccessorImpl引用替换为现在新创建的。下一次 DelegatingMethodAccessorImpl就不会再交给NativeMethodAccessorImpl执行了,而是交给新生成的 java字节码的MethodAccessor。
MethodAccessorGenerator使用了asm字节码动态加载技术,暂不深入研究。
总结 一个方法可以生成多个Method对象,但只有一个root对象,主要用于持有一个MethodAccessor对象,这个对象也可以认 为一个方法只有一个,相当于是static的。因为Method的invoke是交给MethodAccessor执行的,所以我所想要知道的答案在 MethodAccessor的invoke中,深入MethodAccessor:
- 如果底层方法为静态时,那么可以忽略指定的object参数,参数可以为null,
public Object invokeStaticMethod(String className, String methodName,
Object[] args) throws Exception {
Class ownerClass = Class.forName(className);
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Method method = ownerClass.getMethod(methodName,argsClass);
return method.invoke(null, args);
}
- 如果底层方法所需的形参数为0,则提供的args数组长度可以为0或者null
- 如果基本的方法是一个实例方法,它是采用动态方法查找在java语言规范文件调用第二版,第15.12.4.4;特别是,主要基于目标对象的运行时类型会发生。
- 如果基本方法是静态的,则声明该方法的类,如果它还没有被初始化。
- 如果方法正常完成,返回的值返回给调用的调用方;如果该值具有一个原始类型,则首先将其适当地包在一个对象中。然而,如果该值有一个原始类型的数组类型,数组中的元素是空不裹着的物体;换句话说,返回原始类型的数组。如果底层方法返回类型是无效的,则调用返回空值。
Method.invoke()就是调用类中的方法,最简单的用法是可以把方法参数化invoke(class, method)
比如你Test类里有一系列名字相似的方法setValue1、setValue2等等,可以把方法名存进数组v[],
然后循环里invoke(test,v[i]),就顺序调用了全部setValue