深刻理解反射原理

對於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

相關文章
相關標籤/搜索