深入理解反射原理

1,498 阅读2分钟

对于java的用户而言,反射技术对于我们使用来说是非常,下面还是使用一个小的demo为例子 1.首先定一个类:

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

2.定一个测试类

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);
         }
    }

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

方法的反射调用的实现源码: 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,

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.

   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,设置膨胀次数.

总结:
今天通过分析反射调用的源码,一起分析下反射调用过程,