2020-09-04

tech2025-10-26  4

 

目录

JAVA中invoke的详细解析


JAVA中invoke是来自于method类的invoke()方法,他可以完成动态调用

Objectinvoke(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

最新回复(0)