反射踪古,java老司機(jī)應(yīng)該都對(duì)它不陌生,不管是在jvm還是一些大型的框架中盐股,總是能看到它的身影驮审,接下來(lái)我們就來(lái)看看java的反射到底是如何實(shí)現(xiàn)的鲫寄。
前言
在分析具體實(shí)現(xiàn)之前我們還是先來(lái)看個(gè)反射具體使用實(shí)例:
運(yùn)行結(jié)果:
從案例可以看出吉执,我們通過(guò)反射來(lái)獲取對(duì)象定義的相關(guān)內(nèi)容,同時(shí)還可以通過(guò)Method的invoke方法調(diào)用該對(duì)象任何方法地来。想想戳玫,如果在系統(tǒng)運(yùn)行期間大量利用反射來(lái)操作對(duì)象,會(huì)不會(huì)有什么隱患呢未斑?接下來(lái)我們就來(lái)看看java反射的具體實(shí)現(xiàn)咕宿。
Field的獲取
java的反射可通過(guò)Field來(lái)獲取某個(gè)類的屬性或者屬性值,Class類提供以下方法來(lái)獲取Field:
public Field[] getDeclaredFields()
:獲取類聲明的所有屬性蜡秽,包括private屬性府阀;public Field getDeclaredField(String name)
:獲取類聲明的屬性名為name的屬性;public Field[] getFields()
:獲取類聲明的所有public類型屬性芽突;public Field getField(String name)
:獲取類聲明的屬性名為name的public類型屬性肌似。
接下來(lái)我們就以getDeclaredField
方法為例,詳細(xì)分析其源碼實(shí)現(xiàn)诉瓦。
getDeclaredField實(shí)現(xiàn)
從源碼可以看出:
- 校驗(yàn)成員變量是否允許被訪問(wèn):
- 允許被訪問(wèn),跳轉(zhuǎn)到步驟2力细;
- 否則睬澡,拋出SecurityException異常;
- 調(diào)用searchFields方法獲取屬性名為name的field眠蚂;
- 如果步驟2的field為null煞聪,拋出NoSuchFieldException異常,否則逝慧,返回該field昔脯。
獲取Field的邏輯主要在searchFields方法中實(shí)現(xiàn),searchFields方法的第一個(gè)參數(shù)是通過(guò)privateGetDeclaredFields方法從緩存或者JVM獲取到的Fields列表笛臣,接下來(lái)我們來(lái)看看它們的相關(guān)實(shí)現(xiàn)云稚。
privateGetDeclaredFields獲取Fields列表
從源碼可以看出,privateGetDeclaredFields方法首先通過(guò)reflectionData方法從緩存中獲取沈堡,如果從緩存中獲取不到静陈,再調(diào)用Reflection.filterFields方法從JVM中獲取。接下來(lái)我們來(lái)看看reflectionData方法是如何實(shí)現(xiàn)數(shù)據(jù)緩存的诞丽。
reflectionData實(shí)現(xiàn)
從源碼可以看出:
緩存數(shù)據(jù)結(jié)構(gòu):ReflectionData鲸拥,緩存了Field、Method和Constructor等相關(guān)數(shù)據(jù)僧免;
-
整個(gè)reflectionData方法刑赶,首先是從緩存中獲取相關(guān)數(shù)據(jù),需要注意的是懂衩,reflectionData對(duì)象是SoftReference類型的撞叨,該類型的數(shù)據(jù)在內(nèi)存緊張時(shí)會(huì)被回收金踪,如果該對(duì)象被回收,則通過(guò)newReflectionData方法重新創(chuàng)建一個(gè)新的對(duì)象谒所,newReflectionData方法的相關(guān)源碼實(shí)現(xiàn)如下:
newReflectionData實(shí)現(xiàn)
searchFields獲取指定Field
從源碼可以看出热康,searchFields方法在找到指定的Field之后,會(huì)重新copy一份返回劣领,當(dāng)然如果沒(méi)有找到指定Field則返回null姐军。
到這里為止,F(xiàn)ield相關(guān)獲取的實(shí)現(xiàn)就告一段落尖淘,具體通過(guò)Field來(lái)操作對(duì)象相關(guān)部分源碼在本文就不做詳細(xì)講解了奕锌,有興趣的小伙伴可以自行閱讀源碼。接下來(lái)我們就繼續(xù)來(lái)看看Method是如何來(lái)獲取和調(diào)用的村生。
Method的獲取
同F(xiàn)ield一樣惊暴,Class同樣提供四個(gè)方法來(lái)獲取Method:
public Method[] getDeclaredMethods()
:獲取類的所有方法,包括private類型方法趁桃;public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
:獲取類的指定方法名和參數(shù)的方法辽话;public Method[] getMethods()
:獲取類的所有public類型方法;public Method getMethod(String name, Class<?>... parameterTypes)
:獲取類的指定方法名和參數(shù)的public類型方法卫病。
接下來(lái)就以getDeclaredMethod
方法為例油啤,詳細(xì)分析Method的獲取的具體實(shí)現(xiàn)。
getDeclaredMethod實(shí)現(xiàn)
同getDeclaredField方法的流程一樣:
- 校驗(yàn)方法是否允許被訪問(wèn):
- 允許被訪問(wèn)蟀苛,跳轉(zhuǎn)到步驟2益咬;
- 否則,拋出SecurityException異常帜平;
- 調(diào)用searchMethods方法獲取指定方法名和參數(shù)的method幽告;
- 如果步驟2的method為null,拋出NoSuchMethodException異常裆甩,否則冗锁,返回該method。
接下來(lái)我們就來(lái)看看searchMethods方法相關(guān)的具體實(shí)現(xiàn)嗤栓。
注:由于searchMethods方法獲取指定Method同searchFields方法獲取指定Field實(shí)現(xiàn)基本一致蒿讥,在這里就不在做詳細(xì)的分析。
privateGetDeclaredMethods獲取Method列表
同privateGetDeclaredFields方法一樣抛腕,privateGetDeclaredMethods方法同樣通過(guò)reflectionData方法從緩存中獲取Method列表芋绸,如果從緩存到獲取不到,才會(huì)調(diào)用Reflection.filterMethods方法從JVM中獲取担敌。
searchMethods獲取指定Method
同searchFields方法一樣摔敛,searchMethods方法在找到指定的Method之后,同樣會(huì)重新copy一份返回全封。
到這里為止马昙,Method的獲取就簡(jiǎn)單分析完了桃犬,在獲取到Method之后,我們可以通過(guò)其invoke方法來(lái)實(shí)現(xiàn)調(diào)用了行楞,接下來(lái)我們就來(lái)看看invoke方法的具體實(shí)現(xiàn)攒暇。
Method的調(diào)用
從源碼可以看出:
校驗(yàn)該方法是否允許被訪問(wèn),允許被訪問(wèn)則跳轉(zhuǎn)到步驟2子房,否則形用,拋出IllegalAccessException異常;
獲取當(dāng)前method的MethodAccessor對(duì)象ma证杭,如ma為null田度,則調(diào)用acquireMethodAccessor方法獲取解愤;
調(diào)用MethodAccessor對(duì)象的invoke方法來(lái)實(shí)現(xiàn)調(diào)用镇饺,整個(gè)invoke方法的核心也在這里。
接下來(lái)我們就來(lái)看看acquireMethodAccessor方法獲取MethodAccessor對(duì)象以及該對(duì)象的invoke方法的相關(guān)實(shí)現(xiàn)送讲。
acquireMethodAccessor獲取MethodAccessor
從源碼可以看出:
如果root節(jié)點(diǎn)不為空奸笤,則從root節(jié)點(diǎn)獲取MethodAccessor對(duì)象,并且將該對(duì)象賦值給當(dāng)前Method對(duì)象的methodAccessor屬性哼鬓;
如果root節(jié)點(diǎn)為null揭保,或者root節(jié)點(diǎn)的methodAccessor對(duì)象為null時(shí),調(diào)用
reflectionFactory.newMethodAccessor
方法為其創(chuàng)建一個(gè)新的MethodAccessor對(duì)象并返回魄宏。
注:
MethodAccessor定義
從MethodAccessor的定義可以看出,MethodAccessor是一個(gè)接口存筏,那么宠互,上述MethodAccessor對(duì)象其實(shí)也就是MethodAccessor的子類的對(duì)象。
reflectionFactory.newMethodAccessor創(chuàng)建MethodAccessor
在開(kāi)始分析newMethodAccessor方法的實(shí)現(xiàn)之前我們先來(lái)看看ReflectionFactory相關(guān)內(nèi)容椭坚。
-
ReflectionFactory重要屬性
ReflectionFactory相關(guān)屬性
ReflectionFactory有兩個(gè)重要屬性:noInflation(默認(rèn)值為false)和inflationThreshold(默認(rèn)值為15)予跌,它們的重新設(shè)置可以通過(guò)checkInitted方法來(lái)完成,源碼如下圖所示:
checkInitted實(shí)現(xiàn)
從源碼標(biāo)紅框的兩部分可以看出善茎,noInflation和inflationThreshold可以通過(guò)參數(shù)-Dsun.reflect.inflationThreshold
和-Dsun.reflect.noInflation
設(shè)置券册。 -
newMethodAccessor實(shí)現(xiàn)
newMethodAccessor實(shí)現(xiàn)
從源碼可以看出:如果noInflation == true時(shí),調(diào)用MethodAccessorGenerator對(duì)象的generateMethod方法生成MethodAccessorImpl對(duì)象垂涯;
否則烁焙,生成DelegatingMethodAccessorImpl對(duì)象并返回。
MethodAccessor.invoke方法實(shí)現(xiàn)
注:以子類DelegatingMethodAccessorImpl的具體實(shí)現(xiàn)為例耕赘,具體分析invoke方法的相關(guān)實(shí)現(xiàn)骄蝇。
DelegatingMethodAccessorImpl實(shí)現(xiàn)
從源碼可以看出,DelegatingMethodAccessorImpl對(duì)象就是一個(gè)代理對(duì)象操骡,最終的invoke方法其實(shí)也就是調(diào)用NativeMethodAccessorImpl的invoke方法九火。
NativeMethodAccessorImpl實(shí)現(xiàn)
需要注意的是赚窃,如果當(dāng)前invoke方法被調(diào)用的次數(shù)超過(guò)
ReflectionFactory.inflationThreshold
,后續(xù)的調(diào)用就通過(guò)MethodAccessorGenerator
對(duì)象的generateMethod
方法生成MethodAccessorImpl
對(duì)象岔激,并將它賦值給delegate
勒极,這樣下次再調(diào)用Method.invoke
時(shí),調(diào)用的也就是新生成的MethodAccessor
對(duì)象的invoke
方法虑鼎。
generateMethod實(shí)現(xiàn)
注:由于generateMethod實(shí)現(xiàn)代碼比較多辱匿,在這里我就不再貼源碼了,有興趣的小伙伴可以去openjdk看一下相關(guān)源碼實(shí)現(xiàn)震叙。
generateMethod在生成MethodAccessorImpl對(duì)象時(shí)掀鹅,會(huì)生成相應(yīng)的字節(jié)碼并調(diào)用ClassDefiner.defineClass創(chuàng)建對(duì)應(yīng)的對(duì)象。而每一次在調(diào)用ClassDefiner.defineClass創(chuàng)建對(duì)象時(shí)媒楼,都會(huì)生成一個(gè)類加載器乐尊,具體的源碼如下圖所示:
需要注意的是,為什么每次在創(chuàng)建對(duì)象時(shí)都需要生成類加載器呢划址?這么做的主要原因也是為了讓這些生成的類可以被回收扔嵌。稍微了解點(diǎn)兒gc的小伙伴可能都知道,類可以被回收只有在類的加載器可以被回收的情況下才會(huì)被回收夺颤,如果不每次生成新的類加載器痢缎,就可能會(huì)導(dǎo)致新創(chuàng)建的類一直不能被回收。