深刻理解反射原理

对于java的用户而言,反射技术对于咱们使用来讲是很是,下面仍是使用一个小的demo为例子 1.首先定一个类:java

public class Zhangsan implements Man {
    @Override
    public void findObject() {
        System.out.println("oh , i find you.");
    }
}
复制代码

2.定一个测试类bash

public class TestMain {
    public static void main(String[] args) throws Throwable{
         Method method = Class.forName("com.jdk14.demo.dynamic.myjdk.Man").getMethod("findObject");
         Zhangsan zhangsan = new Zhangsan();
        //1.Constructor
         for(int i=0; i < 16; i++) {
             method.invoke(zhangsan);
         }
    }
复制代码

下图就实现了对于对象的调用. markdown

方法的反射调用的实现源码: 1.判断是override,初始化是false,而后对于调用Class的访问检查. 2.判断methodAccessor这个字段的是否为空,第一次调用是空的,而后调用acquireMethodAccessor获取,

public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz,
                        Modifier.isStatic(modifiers) ? null : obj.getClass(),
                        modifiers);
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
    }
复制代码

Method类中有一个属性的root是顶部的Method,初始化是空, 而后就是从reflectionFactory#newMethodAccessor获取到 MethodAccessor,ide

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;
    }
复制代码

能够看出ReflectionFactory是里面单例对象, 测试

经过RefectionFactory#newMethodAccessor方法,返回 DelegatingMethodAccessorImpl这个委托的调用类,里面 开始代理实际是NativeMethodAccessorImpl

public MethodAccessor newMethodAccessor(Method method) {
       checkInitted();
       if (Reflection.isCallerSensitive(method)) {
           Method altMethod = findMethodForReflection(method);
           if (altMethod != null) {
               method = altMethod;
           }
       }
       // use the root Method that will not cache caller class
       Method root = langReflectAccess.getRoot(method);
       if (root != null) {
           method = root;
       }
       if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
           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;
       }
   }
复制代码

而后看下NativeMethodAccessorImpl#invoke方法,这里是JNI调用方式,这里涉及一个概念,调用的膨胀阈值,是ReflectionFactory中inflationThreshold字段,默认是15,当反射调用大于15次,就是经过MethodAccessorGenerator#generateMethod底层是经过ClassFileAssembler这个类生成字节码,这样直接调用方法调用,效率要比Native要高,可是换来是生成class文件,这就是空间换取时间,经过设置parent#setdelegate方法,能够动态替换NativeMethodAccessorImpl为生成的MethodAccessorImpl.ui

public Object invoke(Object obj, Object[] args)
        throws IllegalArgumentException, InvocationTargetException
    {
        // We can't inflate methods belonging to vm-anonymous classes because // that kind of class can't be referred to by name, hence can't be // found from the generated bytecode. // numInvocations默认值是15 if (++numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) { MethodAccessorImpl acc = (MethodAccessorImpl) new MethodAccessorGenerator(). generateMethod(method.getDeclaringClass(), method.getName(), method.getParameterTypes(), method.getReturnType(), method.getExceptionTypes(), method.getModifiers()); parent.setDelegate(acc); } return invoke0(method, obj, args); } 复制代码

反射调用膨胀也可用经过 -Dsun.reflect.noInflation=true,直接打开开关. -Dsun.reflect.inflationThreshold=20,设置膨胀次数.this

总结:
今天经过分析反射调用的源码,一块儿分析下反射调用过程,spa

相关文章
相关标签/搜索