java中的反射

反射踪古,java老司機(jī)應(yīng)該都對(duì)它不陌生,不管是在jvm還是一些大型的框架中盐股,總是能看到它的身影驮审,接下來(lái)我們就來(lái)看看java的反射到底是如何實(shí)現(xiàn)的鲫寄。

前言

在分析具體實(shí)現(xiàn)之前我們還是先來(lái)看個(gè)反射具體使用實(shí)例:


使用案例

運(yùn)行結(jié)果:


運(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)
getDeclaredField實(shí)現(xiàn)

從源碼可以看出:

  1. 校驗(yàn)成員變量是否允許被訪問(wèn):
  • 允許被訪問(wèn),跳轉(zhuǎn)到步驟2力细;
  • 否則睬澡,拋出SecurityException異常;
  1. 調(diào)用searchFields方法獲取屬性名為name的field眠蚂;
  2. 如果步驟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實(shí)現(xiàn)

從源碼可以看出,privateGetDeclaredFields方法首先通過(guò)reflectionData方法從緩存中獲取沈堡,如果從緩存中獲取不到静陈,再調(diào)用Reflection.filterFields方法從JVM中獲取。接下來(lái)我們來(lái)看看reflectionData方法是如何實(shí)現(xiàn)數(shù)據(jù)緩存的诞丽。

reflectionData實(shí)現(xiàn)
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實(shí)現(xiàn)

從源碼可以看出热康,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)
getDeclaredMethod實(shí)現(xiàn)

同getDeclaredField方法的流程一樣:

  1. 校驗(yàn)方法是否允許被訪問(wèn):
  • 允許被訪問(wèn)蟀苛,跳轉(zhuǎn)到步驟2益咬;
  • 否則,拋出SecurityException異常帜平;
  1. 調(diào)用searchMethods方法獲取指定方法名和參數(shù)的method幽告;
  2. 如果步驟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列表
privateGetDeclaredMethods實(shí)現(xiàn)

同privateGetDeclaredFields方法一樣抛腕,privateGetDeclaredMethods方法同樣通過(guò)reflectionData方法從緩存中獲取Method列表芋绸,如果從緩存到獲取不到,才會(huì)調(diào)用Reflection.filterMethods方法從JVM中獲取担敌。

searchMethods獲取指定Method
searchMethods實(shí)現(xiàn)

同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)用

invoke實(shí)現(xiàn)

從源碼可以看出:

  1. 校驗(yàn)該方法是否允許被訪問(wèn),允許被訪問(wèn)則跳轉(zhuǎn)到步驟2子房,否則形用,拋出IllegalAccessException異常;

  2. 獲取當(dāng)前method的MethodAccessor對(duì)象ma证杭,如ma為null田度,則調(diào)用acquireMethodAccessor方法獲取解愤;

  3. 調(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
acquireMethodAccessor實(shí)現(xiàn)

從源碼可以看出:

  • 如果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實(shí)現(xiàn)

從源碼可以看出,DelegatingMethodAccessorImpl對(duì)象就是一個(gè)代理對(duì)象操骡,最終的invoke方法其實(shí)也就是調(diào)用NativeMethodAccessorImpl的invoke方法九火。

NativeMethodAccessorImpl實(shí)現(xiàn)

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è)類加載器乐尊,具體的源碼如下圖所示:


defineClass實(shí)現(xiàn)

需要注意的是,為什么每次在創(chuàng)建對(duì)象時(shí)都需要生成類加載器呢划址?這么做的主要原因也是為了讓這些生成的類可以被回收扔嵌。稍微了解點(diǎn)兒gc的小伙伴可能都知道,類可以被回收只有在類的加載器可以被回收的情況下才會(huì)被回收夺颤,如果不每次生成新的類加載器痢缎,就可能會(huì)導(dǎo)致新創(chuàng)建的類一直不能被回收。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末世澜,一起剝皮案震驚了整個(gè)濱河市独旷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寥裂,老刑警劉巖嵌洼,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異封恰,居然都是意外死亡麻养,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門诺舔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鳖昌,“玉大人,你說(shuō)我怎么就攤上這事低飒⌒碜颍” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵褥赊,是天一觀的道長(zhǎng)车要。 經(jīng)常有香客問(wèn)我,道長(zhǎng)崭倘,這世上最難降的妖魔是什么翼岁? 我笑而不...
    開(kāi)封第一講書人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任类垫,我火速辦了婚禮,結(jié)果婚禮上琅坡,老公的妹妹穿的比我還像新娘悉患。我一直安慰自己,他們只是感情好榆俺,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布售躁。 她就那樣靜靜地躺著,像睡著了一般茴晋。 火紅的嫁衣襯著肌膚如雪陪捷。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 52,549評(píng)論 1 312
  • 那天诺擅,我揣著相機(jī)與錄音市袖,去河邊找鬼。 笑死烁涌,一個(gè)胖子當(dāng)著我的面吹牛苍碟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播撮执,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼微峰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了抒钱?” 一聲冷哼從身側(cè)響起蜓肆,我...
    開(kāi)封第一講書人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谋币,沒(méi)想到半個(gè)月后仗扬,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瑞信,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了穴豫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凡简。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖精肃,靈堂內(nèi)的尸體忽然破棺而出秤涩,到底是詐尸還是另有隱情,我是刑警寧澤司抱,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布筐眷,位于F島的核電站,受9級(jí)特大地震影響习柠,放射性物質(zhì)發(fā)生泄漏匀谣。R本人自食惡果不足惜照棋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望武翎。 院中可真熱鬧烈炭,春花似錦、人聲如沸宝恶。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)垫毙。三九已至霹疫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間综芥,已是汗流浹背丽蝎。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留毫痕,地道東北人征峦。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像消请,于是被迫代替她去往敵國(guó)和親栏笆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 引言:java的高級(jí)特性-反射一直是困擾自己的一個(gè)很大問(wèn)題臊泰,今天專門花了半天再將java中的反射看了一遍蛉加,下面簡(jiǎn)單...
    cp_insist閱讀 2,329評(píng)論 1 6
  • 什么是反射 Java 反射是可以讓我們?cè)谶\(yùn)行時(shí)獲取類的函數(shù)、屬性缸逃、父類针饥、接口等 Class 內(nèi)部信息的機(jī)制。 能做...
    紅燒排骨飯閱讀 540評(píng)論 0 1
  • 概述 Java 反射是可以讓我們?cè)谶\(yùn)行時(shí)獲取類的方法需频、屬性丁眼、父類、接口等類的內(nèi)部信息的機(jī)制昭殉。也就是說(shuō)苞七,反射本質(zhì)上是...
    absfree閱讀 6,250評(píng)論 3 15
  • 生活如戲 戲如生活 一直很想找一個(gè)這樣歷史故事 大概說(shuō)的是: 從前,有一個(gè)能力不俗 忠心耿耿的副將挪丢,一直對(duì)他的將軍...
    梅紫醬閱讀 278評(píng)論 0 0
  • 怎么說(shuō) 不管我再忙再不開(kāi)心 也盡可能盡早回應(yīng) 可是你在朋友圈點(diǎn)贊卻不回微信的行為 我確實(shí)不知道怎么了
    牧七沙閱讀 164評(píng)論 0 0