2020-09-04

 

目录

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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值