Java反射之Method的invoke方法实现[通俗易懂]

Java反射之Method的invoke方法实现[通俗易懂]使用reflect(反射)包下面的Field和Method类获得类的属性和方法,并对属性和方法进行操作。在框架中经常会会用到method.inv

欢迎大家来到IT世界,在知识的湖畔探索吧!

使用reflect(反射)包下面的Field和Method类获得类的属性和方法,并对属性和方法进行操作。

在框架中经常会会用到method.invoke()方法,用来执行某个的对象的目标方法。以前写代码用到反射时,总是获取先获取Method,然后传入对应的Class实例对象执行方法。然而前段时间研究invoke方法时,发现invoke方法居然包含多态的特性,这是以前没有考虑过的一个问题。那么Method.invoke()方法的执行过程是怎么实现的?它的多态又是如何实现的呢?

本文将从java和JVM的源码实现深入探讨invoke方法的实现过程。

首先给出invoke方法多态特性的演示代码:

public class MethodInvoke {	public static void main(String[] args) throws Exception {		Method animalMethod = Animal.class.getDeclaredMethod("print");		Method catMethod = Cat.class.getDeclaredMethod("print");			Animal animal = new Animal();		Cat cat = new Cat();		animalMethod.invoke(cat);		animalMethod.invoke(animal);				catMethod.invoke(cat);		catMethod.invoke(animal);	}} class Animal {	public void print() {		System.out.println("Animal.print()");	}} class Cat extends Animal {	@Override	public void print() {		System.out.println("Cat.print()");	}}

欢迎大家来到IT世界,在知识的湖畔探索吧!

代码中,Cat类覆盖了父类Animal的print()方法, 然后通过反射分别获取print()的Method对象。最后分别用Cat和Animal的实例对象去执行print()方法。其中animalMethod.invoke(animal)和catMethod.invoke(cat),示例对象的真实类型和Method的声明Classs是相同的,按照预期打印结果;animalMethod.invoke(cat)中,由于Cat是Animal的子类,按照多态的特性,子类调用父类的的方法,方法执行时会动态链接到子类的实现方法上。因此,这里会调用Cat.print()方法;而catMethod.invoke(animal)中,传入的参数类型Animal是父类,却期望调用子类Cat的方法,因此这一次会抛出异常。代码打印结果为:

欢迎大家来到IT世界,在知识的湖畔探索吧!Cat.print()Animal.print()Cat.print()Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)	at java.lang.reflect.Method.invoke(Unknown Source)	at com.wy.invoke.MethodInvoke.main(MethodInvoke.java:17)

接下来,我们来看看invoke()方法的实现过程。

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{        if (!override) {            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {                Class<?> caller = Reflection.getCallerClass(1);                 checkAccess(caller, clazz, obj, modifiers);            }        }        MethodAccessor ma = methodAccessor;             // read volatile    if (ma == null) {        ma = acquireMethodAccessor();    }    return ma.invoke(obj, args);}

invoke()方法中主要分为两部分:访问控制检查和调用MethodAccessor.invoke()实现方法执行。

首先看一下访问控制检查这一块的逻辑。第一眼看到这里的逻辑的时候,很容易搞不清楚是干嘛的。通俗来讲就是通过方法的修饰符(public/protected/private/package),来判断方法的调用者是否可以访问该方法。这是java的基础内容,不过用代码写出来,一下子不容易想到。访问控制检查分为3步:

  1. 检查override,如果override为true,跳过检查;否则继续;
  2. 快速检查,判断该方法的修饰符modifiers是否为public,如果是跳过检查;否则继续;
  3. 详细检查,通过方法的(protected/private/package)修饰符或方法的声明类(例如子类可以访问父类的protected方法)与调用者caller之间的关系,判断caller是否有权限访问该方法。

override属性是Method的父类AccessibleObject中声明的变量,使得程序可以控制是否跳过访问权限的检查。另外,Method的实例对象中,override属性的初始值设置为false。

欢迎大家来到IT世界,在知识的湖畔探索吧!public void setAccessible(boolean flag) throws SecurityException {        SecurityManager sm = System.getSecurityManager();        if (sm != null) sm.checkPermission(ACCESS_PERMISSION);        setAccessible0(this, flag);    }     private static void setAccessible0(AccessibleObject obj, boolean flag)        throws SecurityException    {        if (obj instanceof Constructor && flag == true) {            Constructor<?> c = (Constructor<?>)obj;            if (c.getDeclaringClass() == Class.class) {                throw new SecurityException("Can not make a java.lang.Class" +                                        " constructor accessible");        }    }    obj.override = flag;}

多说一句,Field同样继承了AccessibleObject,且Field的override也是初始化为false的,也就是说并没有按照变量的修饰符去初始化不同的值。但是我们在调用Field.set(Object obj, Object value)时,如果该Field是private修饰的,会因没有访问权限而抛出异常,因此必须调用setAccessible(true)。此处非常容易理解为因为变量是public的,所以override就被初始化为true。

invoke()方法中,访问控制检查之后,就是通过MethodAccessor.invoke()调用方法。再来看一下代码:

MethodAccessor ma = methodAccessor;             // read volatileif (ma == null) {    ma = acquireMethodAccessor();}return ma.invoke(obj, args);

这里的逻辑很简单,首先将变量methodAccessor赋值给ma,在方法栈中保存一个可以直接引用的本地变量,如果methodAccessor不存在,调用acquireMethodAccessor()方法创建一个。

    private volatile MethodAccessor methodAccessor;    private Method root;        private MethodAccessor acquireMethodAccessor() {        // First check to see if one has been created yet, and take it        // if so        MethodAccessor tmp = null;        if (root != null) tmp = root.getMethodAccessor();        if (tmp != null) {            methodAccessor = tmp;        } else {            // Otherwise fabricate one and propagate it up to the root            tmp = reflectionFactory.newMethodAccessor(this);            setMethodAccessor(tmp);        }         return tmp;    }     void setMethodAccessor(MethodAccessor accessor) {        methodAccessor = accessor;        // Propagate up        if (root != null) {            root.setMethodAccessor(accessor);        }    }     Method copy() {        Method res = new Method(clazz, name, parameterTypes, returnType,                                exceptionTypes, modifiers, slot, signature,                                annotations, parameterAnnotations, annotationDefault);        res.root = this;        res.methodAccessor = methodAccessor;        return res;    }

综合acquireMethodAccessor(),setMethodAccessor()以及copy()这三个方法,可以看到一个Method实例对象维护了一个root引用。当调用Method.copy()进行方法拷贝时,root指向了被拷贝的对象。那么当一个Method被多次拷贝后,调用一次setMethodAccessor()方法,就会将root引用所指向的Method的methodAccessor变量同样赋值。例如:D -> C -> B -> A,其中X-> Y表示X = Y.copy(), 当C对象调用setMethodAccessor()时,B和A都会传播赋值methodAccessor, 而D的methodAccessor还是null。紧接着,当D需要获取methodAccessor而调用acquireMethodAccessor()时,D获取root的methodAccessor, 那么D将和ABC持有相同的methodAccessor。

虽然Method中,通过维护root引用意图使相同的方法始终保持只有一个methodAccessor实例,但是上述方法仍然无法保证相同的方法只有一个methodAccessor实例。例如通过copy()使ABCD保持关系:D -> C -> B -> A, 当B对象调用setMethodAccessor()时,B和A都会赋值methodAccessor, 而C、D的methodAccessor还是null。这时D调用acquireMethodAccessor()时,D获取root也就是C的methodAccessor,发现为空,然后就新创建了一个。从而出现了相同的方法中出现了两个methodAccessor实例对象的现象。

在Class.getMethod()、Class.getDeclaredMethod()以及Class.getDeclaredMethod(String name, Class<?>… parameterTypes)方法中最终都会调用copy()方法来保障Method使用的安全性。 在比较极端加巧合的情况下,可能会引起类膨胀的问题,这就是接下来要讲到的MethodAccessor的实现机制。

Java反射之Method的invoke方法实现[通俗易懂]

在前面代码中,MethodAccessor的创建是通过反射工厂ReflectionFactory的newMethodAccessor(Method)方法来创建的。

public MethodAccessor newMethodAccessor(Method method) {        checkInitted();     if (noInflation) {        return new MethodAccessorGenerator().            generateMethod(method.getDeclaringClass(),                           method.getName(),                           method.getParameterTypes(),                           method.getReturnType(),                           method.getExceptionTypes(),                           method.getModifiers());    } else {        NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);        DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc);        acc.setParent(res);        return res;    }}

其中, checkInitted()方法检查从配置项中读取配置并设置noInflation、inflationThreshold的值:

熊猫追剧-在线视频网站-海量高清视频免费在线观看

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/17695.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信