在框架中常常会会用到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 {@Overridepublic void print() {System.out.println("Cat.print()");}}
代码中,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的方法,因此这一次会抛出非常。代码打印结果为:
Cat.print()Animal.print()Cat.print()Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring classat 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步:
检讨override,如果override为true,跳过检讨;否则连续;快速检讨,判断该方法的润色符modifiers是否为public,如果是跳过检讨;否则连续;详细检讨,通过方法的(protected/private/package)润色符或方法的声明类(例如子类可以访问父类的protected方法)与调用者caller之间的关系,判断caller是否有权限访问该方法。override属性是Method的父类AccessibleObject中声明的变量,使得程序可以掌握是否跳过访问权限的检讨。其余,Method的实例工具中,override属性的初始值设置为false。
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的实现机制。
在前面代码中,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的值:
熊猫追剧-在线视频网站-海量高清视频免费在线不雅观看