反射及其原理

目錄

image.png

例子

  • 定義一個(gè)User類
public class User {
    private String name;
    private Integer age;
    // 省略get set
}
反射設(shè)置屬性
public class Test {


    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("User");
        testReflection(clazz);
    }

    /**
     * 如果遇到多個(gè)類都有name屬性并需要提供統(tǒng)一處理這時(shí)候就很好用
     * @param clazz
     */
    public static void testReflection(Class clazz) {
        try {
            if (clazz == null) {
                System.out.println("空 clazz");
                return;
            }
            //獲得類的實(shí)例
            Object obj = clazz.newInstance();
            //獲得 User 類中的指定屬性對應(yīng)的Field對象(每個(gè)屬性對應(yīng)一個(gè)Field對象)
            Field field = clazz.getDeclaredField("name");

            //取消屬性的訪問權(quán)限控制,即使private 屬性也可以進(jìn)行訪問
            field.setAccessible(true);
            //調(diào)用 getter 方法獲取屬性值
            System.out.println(field.get(obj));
            //調(diào)用setter 方法給屬性賦值
            field.set(obj, "scott");
            //調(diào)用 getter 方法獲取對應(yīng)屬性修改后的值
            System.out.println(field.get(obj));
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("異常");
        }

    }


}
反射執(zhí)行方法
public static void main(String[] args) throws Exception {
    Class clazz = User.class;
    Object obj = clazz.newInstance();


    //調(diào)用該對象的 setName方法
    Method method = clazz.getMethod("setName", new Class[]{String.class});
    Object result = method.invoke(obj, new Object[]{"scott"});       //  obj.setName("scott");
    System.out.println("返回值為:" + result);

    //調(diào)用對象的getName()方法
    Method method1 = clazz.getMethod("getName", new Class[]{});
    Object obj1 = method1.invoke(obj, new Object[]{});
    System.out.println("返回值為:" + obj1);

}

原理

  • 代碼示例
Class<?> clazz = Class.forName("TestOOM");
TestOOM testOOM = (TestOOM) clazz1.getDeclaredConstructor().newInstance();
Method method = clazz.getDeclaredMethod("test");
method.invoke(testOOM1);
  • 先看下method的invoke方法,Method實(shí)例對象維護(hù)了一個(gè)root引用。當(dāng)調(diào)用Method.copy()進(jìn)行方法拷貝時(shí),root指向了被拷貝的對象,通過維護(hù)root引用意圖使相同的方法始終保持只有一個(gè)methodAccessor實(shí)例。ma.invoke會(huì)先通過DelegatingMethodAccessorImpl.invoke,再到NativeMethodAccessorImpl.invoke,NativeMethodAccessorImpl最終調(diào)用native方法完成invoke()任務(wù)。
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException,InvocationTargetException{
    // 檢查override,如果override為true,跳過檢查
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    // private volatile MethodAccessor methodAccessor;
    MethodAccessor ma = methodAccessor;          
    if (ma == null) {
        // ma不存在則去獲取
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
}

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

public MethodAccessor newMethodAccessor(Method method) {
    checkInitted();
    // 啟動(dòng)參數(shù)-Dsun.reflect.noInflation
    if (noInflation) {
        //省略部分部分
    } else {
        NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
        // 委托模式
        DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc);
        acc.setParent(res);
        return res;
    }
}

void setMethodAccessor(MethodAccessor accessor) {
    methodAccessor = accessor;
    // Propagate up
    if (root != null) {
        root.setMethodAccessor(accessor);
    }
}

// 每次獲取Method都是new的,類似cow思想
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;
}
  • NativeMethodAccessorImpl最終調(diào)用native方法完成invoke()任務(wù)。當(dāng)numInvocations數(shù)量大于配置項(xiàng)sun.reflect.inflationThreshold(m默認(rèn)15)即類膨脹閾值時(shí), 使用MethodAccessorGenerator創(chuàng)建一個(gè)代理類對象,并且將被委托的NativeMethodAccessorImpl的parent??傮w來說,當(dāng)調(diào)用invoke()時(shí),按照默認(rèn)配置,Method首先創(chuàng)建一個(gè)DelegatingMethodAccessorImpl對象,并設(shè)置一個(gè)被委托的NativeMethodAccessorImpl對象,那么method.invoke()就被轉(zhuǎn)換成DelegatingMethodAccessorImpl.invoke(),然后又被委托給NativeMethodAccessorImp.invoke()實(shí)現(xiàn)。當(dāng)NativeMethodAccessorImp.invoke()調(diào)用次數(shù)超過一定熱度時(shí)(默認(rèn)15次),被委托方又被轉(zhuǎn)換成代理類來實(shí)現(xiàn)。圖中DelegatingMethodAccessorImpl用到代碼模式,可能代理1,也可能代理2,代理1到一定條件時(shí),代理1通過this.parent.setDelegate更換DelegatingMethodAccessorImpl代理的類。


    image.png
public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException {
    if (++numInvocations > ReflectionFactory.inflationThreshold()) {
        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);
}

// JNI調(diào)用 
private static native Object invoke0(Method m, Object obj, Object[] args);
  • 接下來看看getDeclaredMethod, 這里特別注意privateGetDeclaredMethods,其中涉及軟引用,如果getDeclaredMethod用到軟引用緩存的,上面的MethodAccessor就是同一個(gè),即使并發(fā)導(dǎo)致生成多個(gè)GeneratedMethodAccessorXXX(GeneratedMethodAccessorXXX的類加載器其實(shí)是一個(gè)DelegatingClassLoader類加載器, 這樣更方便卸載)也生成的不大;但是如果非軟引用而是重新生成的,那就好大量有DelegatingClassLoader加載,GeneratedMethodAccessorXXX大量加載卸載的問題。
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
    checkInitted();
    Method[] res;
    // 軟引用
    ReflectionData<T> rd = reflectionData();
    if (rd != null) {
        res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
        if (res != null) return res;
    }
    // No cached value available; request value from VM
    res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
    if (rd != null) {
        if (publicOnly) {
            rd.declaredPublicMethods = res;
        } else {
            rd.declaredMethods = res;
        }
    }
    return res;
}
  • 總結(jié): Method.invoke的邏輯,相關(guān)的method因此被SoftReference引用,因此很容易被回收,一旦被回收,那就創(chuàng)建一個(gè)新的Method對象,再調(diào)用其invoke方法,在調(diào)用到一定次數(shù)(15次)之后,就構(gòu)建一個(gè)新的字節(jié)碼類,伴隨著GC的進(jìn)行,同一個(gè)方法的字節(jié)碼類不斷構(gòu)建,直到將Perm充滿觸發(fā)一次Full GC才得以釋放

Class.forName 和 ClassLoader區(qū)別

  • 在java中Class.forName()和ClassLoader都可以對類進(jìn)行加載。ClassLoader就是遵循雙親委派模型最終調(diào)用啟動(dòng)類加載器的類加載器,實(shí)現(xiàn)的功能是“通過一個(gè)類的全限定名來獲取描述此類的二進(jìn)制字節(jié)流”,獲取到二進(jìn)制流后放到JVM中。Class.forName()方法實(shí)際上也是調(diào)用的CLassLoader來實(shí)現(xiàn)的。
  • Spring框架中的IOC的實(shí)現(xiàn)就是使用的ClassLoader,JDBC時(shí)通常是使用Class.forName()方法來加載數(shù)據(jù)庫連接驅(qū)動(dòng)。這是因?yàn)樵贘DBC規(guī)范中明確要求Driver(數(shù)據(jù)庫驅(qū)動(dòng))類必須向DriverManager注冊自己。

設(shè)計(jì)模式學(xué)習(xí)

  • DelegatingMethodAccessorImpl就是用到委托模式,setDelegate, parent等關(guān)鍵字還可以改變委托的對象。委托模式是一種更高級的代理模式,委托模式可以解決一種方案的多種實(shí)現(xiàn)之間自由切換,而代理模式只能根據(jù)傳入的被代理對象來實(shí)現(xiàn)功能。
  • 結(jié)構(gòu)型之代理模式
  • 裝飾器模式

反射和序列化破壞單例解決方案

優(yōu)化

  • 可以把查找到的緩存起來。反射調(diào)用時(shí)本身就有用弱引用進(jìn)行緩存。

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容