反射,java老司機應該都對它不陌生,不管是在jvm還是一些大型的框架中,總是能看到它的身影,接下來我們就來看看java的反射到底是如何實現的。
前言
在分析具體實現之前我們還是先來看個反射具體使用實例:
運行結果:
從案例可以看出,我們通過反射來獲取對象定義的相關內容,同時還可以通過Method的invoke方法調用該對象任何方法。想想,如果在系統運行期間大量利用反射來操作對象,會不會有什么隱患呢?接下來我們就來看看java反射的具體實現。
Field的獲取
java的反射可通過Field來獲取某個類的屬性或者屬性值,Class類提供以下方法來獲取Field:
public Field[] getDeclaredFields()
:獲取類聲明的所有屬性,包括private屬性;public Field getDeclaredField(String name)
:獲取類聲明的屬性名為name的屬性;public Field[] getFields()
:獲取類聲明的所有public類型屬性;public Field getField(String name)
:獲取類聲明的屬性名為name的public類型屬性。
接下來我們就以getDeclaredField
方法為例,詳細分析其源碼實現。
getDeclaredField實現
從源碼可以看出:
- 校驗成員變量是否允許被訪問:
- 允許被訪問,跳轉到步驟2;
- 否則,拋出SecurityException異常;
- 調用searchFields方法獲取屬性名為name的field;
- 如果步驟2的field為null,拋出NoSuchFieldException異常,否則,返回該field。
獲取Field的邏輯主要在searchFields方法中實現,searchFields方法的第一個參數是通過privateGetDeclaredFields方法從緩存或者JVM獲取到的Fields列表,接下來我們來看看它們的相關實現。
privateGetDeclaredFields獲取Fields列表
從源碼可以看出,privateGetDeclaredFields方法首先通過reflectionData方法從緩存中獲取,如果從緩存中獲取不到,再調用Reflection.filterFields方法從JVM中獲取。接下來我們來看看reflectionData方法是如何實現數據緩存的。
reflectionData實現
從源碼可以看出:
緩存數據結構:ReflectionData,緩存了Field、Method和Constructor等相關數據;
-
整個reflectionData方法,首先是從緩存中獲取相關數據,需要注意的是,reflectionData對象是SoftReference類型的,該類型的數據在內存緊張時會被回收,如果該對象被回收,則通過newReflectionData方法重新創建一個新的對象,newReflectionData方法的相關源碼實現如下:
newReflectionData實現
searchFields獲取指定Field
從源碼可以看出,searchFields方法在找到指定的Field之后,會重新copy一份返回,當然如果沒有找到指定Field則返回null。
到這里為止,Field相關獲取的實現就告一段落,具體通過Field來操作對象相關部分源碼在本文就不做詳細講解了,有興趣的小伙伴可以自行閱讀源碼。接下來我們就繼續來看看Method是如何來獲取和調用的。
Method的獲取
同Field一樣,Class同樣提供四個方法來獲取Method:
public Method[] getDeclaredMethods()
:獲取類的所有方法,包括private類型方法;public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
:獲取類的指定方法名和參數的方法;public Method[] getMethods()
:獲取類的所有public類型方法;public Method getMethod(String name, Class<?>... parameterTypes)
:獲取類的指定方法名和參數的public類型方法。
接下來就以getDeclaredMethod
方法為例,詳細分析Method的獲取的具體實現。
getDeclaredMethod實現
同getDeclaredField方法的流程一樣:
- 校驗方法是否允許被訪問:
- 允許被訪問,跳轉到步驟2;
- 否則,拋出SecurityException異常;
- 調用searchMethods方法獲取指定方法名和參數的method;
- 如果步驟2的method為null,拋出NoSuchMethodException異常,否則,返回該method。
接下來我們就來看看searchMethods方法相關的具體實現。
注:由于searchMethods方法獲取指定Method同searchFields方法獲取指定Field實現基本一致,在這里就不在做詳細的分析。
privateGetDeclaredMethods獲取Method列表
同privateGetDeclaredFields方法一樣,privateGetDeclaredMethods方法同樣通過reflectionData方法從緩存中獲取Method列表,如果從緩存到獲取不到,才會調用Reflection.filterMethods方法從JVM中獲取。
searchMethods獲取指定Method
同searchFields方法一樣,searchMethods方法在找到指定的Method之后,同樣會重新copy一份返回。
到這里為止,Method的獲取就簡單分析完了,在獲取到Method之后,我們可以通過其invoke方法來實現調用了,接下來我們就來看看invoke方法的具體實現。
Method的調用
從源碼可以看出:
校驗該方法是否允許被訪問,允許被訪問則跳轉到步驟2,否則,拋出IllegalAccessException異常;
獲取當前method的MethodAccessor對象ma,如ma為null,則調用acquireMethodAccessor方法獲取;
調用MethodAccessor對象的invoke方法來實現調用,整個invoke方法的核心也在這里。
接下來我們就來看看acquireMethodAccessor方法獲取MethodAccessor對象以及該對象的invoke方法的相關實現。
acquireMethodAccessor獲取MethodAccessor
從源碼可以看出:
如果root節點不為空,則從root節點獲取MethodAccessor對象,并且將該對象賦值給當前Method對象的methodAccessor屬性;
如果root節點為null,或者root節點的methodAccessor對象為null時,調用
reflectionFactory.newMethodAccessor
方法為其創建一個新的MethodAccessor對象并返回。
注:
MethodAccessor定義
從MethodAccessor的定義可以看出,MethodAccessor是一個接口,那么,上述MethodAccessor對象其實也就是MethodAccessor的子類的對象。
reflectionFactory.newMethodAccessor創建MethodAccessor
在開始分析newMethodAccessor方法的實現之前我們先來看看ReflectionFactory相關內容。
-
ReflectionFactory重要屬性
ReflectionFactory相關屬性
ReflectionFactory有兩個重要屬性:noInflation(默認值為false)和inflationThreshold(默認值為15),它們的重新設置可以通過checkInitted方法來完成,源碼如下圖所示:
checkInitted實現
從源碼標紅框的兩部分可以看出,noInflation和inflationThreshold可以通過參數-Dsun.reflect.inflationThreshold
和-Dsun.reflect.noInflation
設置。 -
newMethodAccessor實現
newMethodAccessor實現
從源碼可以看出:如果noInflation == true時,調用MethodAccessorGenerator對象的generateMethod方法生成MethodAccessorImpl對象;
否則,生成DelegatingMethodAccessorImpl對象并返回。
MethodAccessor.invoke方法實現
注:以子類DelegatingMethodAccessorImpl的具體實現為例,具體分析invoke方法的相關實現。
DelegatingMethodAccessorImpl實現
從源碼可以看出,DelegatingMethodAccessorImpl對象就是一個代理對象,最終的invoke方法其實也就是調用NativeMethodAccessorImpl的invoke方法。
NativeMethodAccessorImpl實現
需要注意的是,如果當前invoke方法被調用的次數超過
ReflectionFactory.inflationThreshold
,后續的調用就通過MethodAccessorGenerator
對象的generateMethod
方法生成MethodAccessorImpl
對象,并將它賦值給delegate
,這樣下次再調用Method.invoke
時,調用的也就是新生成的MethodAccessor
對象的invoke
方法。
generateMethod實現
注:由于generateMethod實現代碼比較多,在這里我就不再貼源碼了,有興趣的小伙伴可以去openjdk看一下相關源碼實現。
generateMethod在生成MethodAccessorImpl對象時,會生成相應的字節碼并調用ClassDefiner.defineClass創建對應的對象。而每一次在調用ClassDefiner.defineClass創建對象時,都會生成一個類加載器,具體的源碼如下圖所示:
需要注意的是,為什么每次在創建對象時都需要生成類加載器呢?這么做的主要原因也是為了讓這些生成的類可以被回收。稍微了解點兒gc的小伙伴可能都知道,類可以被回收只有在類的加載器可以被回收的情況下才會被回收,如果不每次生成新的類加載器,就可能會導致新創建的類一直不能被回收。