Java 反射機制(二)

前言

在上篇 Java 反射機制(一) 介紹了一些 Java 反射相關的經常使用 API ,在知道了如何去使用反射以後,做爲一個合格的工程師,下一步確定是要去了解它的如何實現的,咱們今天就來看看在 JDK 源碼中是如何去實現反射的(PS:如下源碼分析基於 JDK1.8)。html

Field 類 set 方法的實現

Field 類的 set 方法是在運行時用來動態修改一個類的屬性的值,進入到 Field 類的 set 方法的源碼以下:java

public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException {
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    getFieldAccessor(obj).set(obj, value);
}
複製代碼

首先根據 override 判斷是否須要檢查字段的訪問權限,而後經過 getFieldAccessor 方法得到一個 FieldAccessor 字段訪問者對象,最後調用的是 FieldAccessor 類的 set 方法進行下一步操做的,FieldAccessor 是一個接口,定義了對字段的一些操做,該接口有以下一些實現類:緩存

fieldaccessor_implements.png

要看 set 到底調用的是哪一個實現類的方法,那麼咱們須要看看 getFieldAccessor() 返回的是哪一個類的對象,下面是 getFieldAccessor 方法的源碼實現:安全

// security check is done before calling this method
private FieldAccessor getFieldAccessor(Object obj) throws IllegalAccessException {
    boolean ov = override;
    FieldAccessor a = (ov) ? overrideFieldAccessor : fieldAccessor;
    return (a != null) ? a : acquireFieldAccessor(ov);
}
複製代碼

這裏先經過 override 來獲取不一樣的緩存的 FieldAccessor,其中 overrideFieldAccessor 表明本類覆蓋父類的字段訪問者對象緩存,fieldAccessor 是本類的字段訪問器對象緩存。若是緩存存在的話就直接複用以前的對象,不然就調用 Field 類的 acquireFieldAccessor 方法獲取。咱們進入到 acquireFieldAccessor 方法中看看,方法源碼以下:ide

private FieldAccessor acquireFieldAccessor(boolean overrideFinalCheck) {
    // First check to see if one has been created yet, and take it
    // if so
    FieldAccessor tmp = null;
    if (root != null) tmp = root.getFieldAccessor(overrideFinalCheck);
    if (tmp != null) {
        if (overrideFinalCheck)
            overrideFieldAccessor = tmp;
        else
            fieldAccessor = tmp;
    } else {
        // Otherwise fabricate one and propagate it up to the root
        tmp = reflectionFactory.newFieldAccessor(this, overrideFinalCheck);
        setFieldAccessor(tmp, overrideFinalCheck);
    }

    return tmp;
}
複製代碼

acquireFieldAccessor 的源碼中咱們能夠看到,先判斷是否已存在 FieldAccessor 對象,若是存在的話那麼就會複用以前的 FieldAccessor 對象,不然就使用 reflectionFactory 工廠的 newFieldAccessor 方法生成一個新的 FieldAccessor 對象出來。因此咱們就要進到 newFieldAccessor 方法裏面看看是如何生成的,方法源碼以下:源碼分析

public FieldAccessor newFieldAccessor(Field var1, boolean var2) {
    checkInitted();
    return UnsafeFieldAccessorFactory.newFieldAccessor(var1, var2);
}
複製代碼

newFieldAccessor 方法代碼能夠得知,在方法裏面是經過 UnsafeFieldAccessorFactory 類的 static 方法 newFieldAccessor 來生產 FieldAccessor 的,那麼咱們繼續進入到 UnsafeFieldAccessorFactory 類的 newFieldAccessor 方法裏面看看,方法源碼以下:post

static FieldAccessor newFieldAccessor(Field var0, boolean var1) {
    Class var2 = var0.getType();
    boolean var3 = Modifier.isStatic(var0.getModifiers());
    boolean var4 = Modifier.isFinal(var0.getModifiers());
    boolean var5 = Modifier.isVolatile(var0.getModifiers());
    boolean var6 = var4 || var5;
    boolean var7 = var4 && (var3 || !var1);
    if (var3) {
      UnsafeFieldAccessorImpl.unsafe.ensureClassInitialized(var0.getDeclaringClass());
      if (!var6) {
        if (var2 == Boolean.TYPE) {
          return new UnsafeStaticBooleanFieldAccessorImpl(var0);
        } else if (var2 == Byte.TYPE) {
          return new UnsafeStaticByteFieldAccessorImpl(var0);
        } else if (var2 == Short.TYPE) {
          return new UnsafeStaticShortFieldAccessorImpl(var0);
        } else if (var2 == Character.TYPE) {
          return new UnsafeStaticCharacterFieldAccessorImpl(var0);
        } else if (var2 == Integer.TYPE) {
          return new UnsafeStaticIntegerFieldAccessorImpl(var0);
        } else if (var2 == Long.TYPE) {
          return new UnsafeStaticLongFieldAccessorImpl(var0);
        } else if (var2 == Float.TYPE) {
          return new UnsafeStaticFloatFieldAccessorImpl(var0);
        } else {
          return (FieldAccessor)(var2 == Double.TYPE ? new UnsafeStaticDoubleFieldAccessorImpl(var0) : new UnsafeStaticObjectFieldAccessorImpl(var0));
        }
      }

      // 剩下的部分省略...

    }   
}       
複製代碼

從以上 UnsafeFieldAccessorFactory 類的 newFieldAccessor 方法代碼能夠看出,方法裏面經過類的字段修飾符類型和字段的類類型共同決定返回的 FieldAccessor 實現類,這裏要注意一下方法裏面這幾個變量的含義:性能

  • var3(isStatic):靜態屬性,也就是 static 關鍵字修飾的屬性。
  • var4(isFinal):final 關鍵字修飾的屬性。
  • var5(isVolatile):valatile 關鍵字修飾的屬性。
  • var6(isQualified):valatile 關鍵字或者 final 關鍵字修飾的屬性。
  • var7 (isReadOnly):是否只讀屬性,final 關鍵字修飾的屬性或者 static 關鍵字修飾而且不能覆蓋(override = false)的屬性

舉一個例子,假設在一個類中的字段聲明爲 public static String name,那麼返回的字段訪問器爲 UnsafeStaticCharacterFieldAccessorImpl,咱們看看這個類的 set 方法是如何實現的,方法源碼以下:ui

public void set(Object var1, Object var2) throws IllegalArgumentException, IllegalAccessException {
    if (this.isFinal) {
      this.throwFinalFieldIllegalAccessException(var2);
    }

    if (var2 == null) {
      this.throwSetIllegalArgumentException(var2);
    }

    if (var2 instanceof Character) {
      unsafe.putChar(this.base, this.fieldOffset, (Character)var2);
    } else {
      this.throwSetIllegalArgumentException(var2);
    }
}
複製代碼

從上面方法的代碼得知,方法最終仍是經過 Unsafe 類的 native 方法 putChar(Object var1, long var2, char var4) 來實現的,有關 Unsafe 類的介紹請看這篇文章(Java魔法類:Unsafe應用解析)。this

Method 類 invoke 方法的實現

Method 類的 invoke 方法用來在運行時動態調用對象的方法,咱們進入到 Method 類的 invoke 方法中看看在 JDK 中究竟是怎麼作的,方法源碼以下:

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    MethodAccessor ma = methodAccessor;             // read volatile
    if (ma == null) {
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
}
複製代碼

從以上方法代碼咱們能夠看到,和上文說的的 Field 類同樣,首先也是先根據 override 進行了一些權限檢查,最後調用的是 MethodAccessorinvoke 方法進行處理,這個方法訪問器 MethodAccessor 是一個接口,它只有一個操做方法調用的 invoke 方法,它有以下三個實現類:

methodaccessor_implements.png

要想知道 ma.invoke 具體調用的是哪一個類的方法,咱們須要看看方法 acquireMethodAccessor 返回的對象是哪一個,該方法的源碼以下:

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;
}
複製代碼

從以上方法 acquireMethodAccessor 的源碼能夠看出,首先會先先判斷是否已經存在了對應的 MethodAccessor 對象,若是有就會複用這個對象,不然就調用工廠 reflectionFactorynewMethodAccessor 方法生成一個 MethodAccessor 對象出來。那麼咱們就須要進入到方法 newMethodAccessor 中,方法源碼以下:

public MethodAccessor newMethodAccessor(Method var1) {
    checkInitted();
    if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
      return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
    } else {
      NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
      DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
      var2.setParent(var3);
      return var3;
    }
}
複製代碼

從方法 newMethodAccessor 的代碼能夠看到,方法首先是使用 Method 對象做爲入參生成了 NativeMethodAccessorImpl 對象,而後再使用 NativeMethodAccessorImpl 對象做爲入參生成了 DelegatingMethodAccessorImpl 對象。這個使用了代理模式,將 NativeMethodAccessorImpl 交給了 DelegatingMethodAccessorImpl 類進行了代理,進入到代理類 DelegatingMethodAccessorImpl 中能夠看到:

delegatingmethodaccessorimpl.png

從上面的紅色方框能夠看到,在類 DelegatingMethodAccessorImpl 的構造方法中將參數賦值給類中的 delegate 屬性,全部上所說的 ma.invoke 最終會進入到 DelegatingMethodAccessorImpl 代理類的 invoke,方法裏調用的是 delegate 屬性的 invoke 方法,該屬性聲明的類型爲抽象類 MethodAccessorImpl,它有以下兩個實現類:

methodaccessorimpl_implements.png

按照上文所說的,這裏的 delegate 屬性是 NativeMethodAccessorImpl 對象,那麼就進入到 NativeMethodAccessorImplinvoke 方法中,方法源碼以下:

public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
    if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
      MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
      this.parent.setDelegate(var3);
    }

    return invoke0(this.method, var1, var2);
}
複製代碼

NativeMethodAccessorImplinvoke 方法會先判斷這次調用是否超過 ReflectionFactory.inflationThreshold() 方法返回的閾值(PS:默認的閾值大小爲 15),若是超過了該閾值,則使用方法訪問生成器從新生成一個 MethodAccessorImpl,並將 DelegatingMethodAccessorImpldelegate 屬性指向這個新生成的 MethodAccessorImpl 對象。從 Reflectionfactory 工廠類的一下注釋:

reflectionfactory_doc.png

能夠得知 JVM 初次加載字節碼實現反射的時候,使用 Method.invokeConstructor.newInstance 方式加載所花費的時間是使用原生代碼加載所花費的時間的 3 - 4 倍。這也就是咱們日常說爲何頻繁使用反射的應用須要花費更多的時間。JVM 做者們爲了不這種花費較長的加載時間,咱們在第一次加載的時候重用了 JVM 的入口,以後切換到字節碼實現的實現。 正如註釋所述,在 MethodAccessor 接口的實現中,有兩個不一樣的版本,一個 Java 實現的,一個是 Native 實現的。Java 版本實現的版本在初始化的時須要比較多的時間,但長久來講性能會更好一些;而 Native 版本則正好相反,在初始化時相對較快,可是在運行一段時間以後性能就不如 Java 版本的了。爲了權衡兩種版本的特性,sun 公司的 JDK 使用了 inflation 機制,讓 Java 方法在被反射調用時,開頭的幾回調用使用 native 版,等反射調用次數超過閾值時則生成一個專用的 MethodAccessor 實現類,生成其中的 invoke 方法的字節碼,之後對該 Java 方法的反射調用就會使用 Java 版。

總結

本文主要介紹反射調用 set(Object obj, Object value) 方法和 invoke(Object obj, Object... args) 方法的底層實現,因爲水平有限本人暫時尚未能力分析 JVM 的實現,這裏只分析到最終 native 方法的調用。底層會依賴到 Unsafe 類來執行一些低級別、不安全操做的方法,如直接訪問系統內存資源、自主管理內存資源等,這些方法在提高 Java 運行效率、加強 Java 語言底層資源操做能力方面起到了很大的做用。對於屬性反射的方法 setXXXgetXXX 的實現分別對應 Unsafe 類的 putXXXgetXXX 方法,也就是說徹底依賴 Unsafe 類中的 native 方法來實現的;對於方法反射的方法 invoke 底層調用的是 NativeMethodAccessorImpl 類的 invoke0native 方法來實現的。對於反射構造器調用的實現,讀者能夠本身進入其源碼進行分析,大致上和反射方法調用的實現相似。


參考文章

相關文章
相關標籤/搜索