Java方法反射實(shí)現(xiàn)原理詳解
博主說:Java 反射機(jī)制是在運(yùn)行狀態(tài)中,對于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對于任意一個(gè)對象,都能夠調(diào)用它的任意方法和屬性;這種動(dòng)態(tài)獲取信息以及動(dòng)態(tài)調(diào)用對象方法的功能稱為 Java 語言的反射機(jī)制。在本文中,占小狼分析了 Java 反射機(jī)制的實(shí)現(xiàn)原理(源碼),感興趣的同學(xué)可以通過閱讀本文花上幾分鐘了解了解。
正文
方法反射實(shí)例
public class ReflectCase { public static void main(String[] args) throws Exception { Proxy target = new Proxy(); Method method = Proxy.class.getDeclaredMethod("run"); method.invoke(target); } static class Proxy { public void run() { System.out.println("run"); } } }
通過 Java 的反射機(jī)制,可以在運(yùn)行期間調(diào)用對象的任何方法,如果大量使用這種方式進(jìn)行調(diào)用,會(huì)有性能或內(nèi)存隱患么?為了徹底了解方法的反射機(jī)制,只能從底層代碼入手啦!
Method 獲取
調(diào)用 Class 類的getDeclaredMethod可以獲取指定方法名和參數(shù)的方法對象 Method。
getDeclaredMethod
其中privateGetDeclaredMethods方法從緩存或 JVM 中獲取該 Class 中申明的方法列表,searchMethods方法將從返回的方法列表里找到一個(gè)匹配名稱和參數(shù)的方法對象。
searchMethods
如果找到一個(gè)匹配的 Method,則重新復(fù)制一份返回,即Method.copy()
方法。
所次每次調(diào)用getDeclaredMethod方法返回的 Method 對象其實(shí)都是一個(gè)新的對象,且新對象的root屬性都指向原來的 Method 對象,如果需要頻繁調(diào)用,最好把 Method 對象緩存起來。
privateGetDeclaredMethods
從緩存或 JVM 中獲取該 Class 中申明的方法列表,實(shí)現(xiàn)如下:
其中reflectionData()方法實(shí)現(xiàn)如下:
這里有個(gè)比較重要的數(shù)據(jù)結(jié)構(gòu)ReflectionData,用來緩存從 JVM 中讀取類的如下屬性數(shù)據(jù):
從reflectionData()方法實(shí)現(xiàn)可以看出:reflectionData對象是SoftReference類型的,說明在內(nèi)存緊張時(shí)可能會(huì)被回收,不過也可以通過-XX:SoftRefLRUPolicyMSPerMB參數(shù)控制回收的時(shí)機(jī),只要發(fā)生GC就會(huì)將其回收,如果reflectionData被回收之后,又執(zhí)行了反射方法,那只能通過newReflectionData方法重新創(chuàng)建一個(gè)這樣的對象了,newReflectionData方法實(shí)現(xiàn)如下:
通過unsafe.compareAndSwapObject方法重新設(shè)置reflectionData字段;在privateGetDeclaredMethods方法中,如果通過reflectionData()獲得的ReflectionData對象不為空,則嘗試從ReflectionData對象中獲取declaredMethods屬性,如果是第一次,或則被GC回收之后,重新初始化后的類屬性為空,則需要重新到 JVM 中獲取一次,并賦值給ReflectionData,下次調(diào)用就可以使用緩存數(shù)據(jù)了。
Method 調(diào)用
獲取到指定的方法對象 Method 之后,就可以調(diào)用它的invoke方法了,invoke實(shí)現(xiàn)如下:
應(yīng)該注意到:這里的MethodAccessor對象是invoke方法實(shí)現(xiàn)的關(guān)鍵,一開始methodAccessor為空,需要調(diào)用acquireMethodAccessor生成一個(gè)新的MethodAccessor對象,MethodAccessor本身就是一個(gè)接口,實(shí)現(xiàn)如下:
在acquireMethodAccessor方法中,會(huì)通過ReflectionFactory類的newMethodAccessor創(chuàng)建一個(gè)實(shí)現(xiàn)了MethodAccessor接口的對象,實(shí)現(xiàn)如下:
在ReflectionFactory類中,有 2 個(gè)重要的字段:noInflation(默認(rèn)false)和inflationThreshold(默認(rèn)15),在checkInitted方法中可以通過-Dsun.reflect.inflationThreshold=xxx和-Dsun.reflect.noInflation=true對這兩個(gè)字段重新設(shè)置,而且只會(huì)設(shè)置一次;如果noInflation為false,方法newMethodAccessor都會(huì)返回DelegatingMethodAccessorImpl對象,DelegatingMethodAccessorImpl的類實(shí)現(xiàn):
其實(shí),DelegatingMethodAccessorImpl對象就是一個(gè)代理對象,負(fù)責(zé)調(diào)用被代理對象delegate的invoke方法,其中delegate參數(shù)目前是NativeMethodAccessorImpl對象,所以最終 Method 的invoke方法調(diào)用的是NativeMethodAccessorImpl對象invoke方法,實(shí)現(xiàn)如下:
這里用到了ReflectionFactory類中的inflationThreshold,當(dāng)delegate調(diào)用了15次invoke方法之后,如果繼續(xù)調(diào)用就通過MethodAccessorGenerator類的generateMethod方法生成MethodAccessorImpl對象,并設(shè)置為delegate對象,這樣下次執(zhí)行Method.invoke時(shí),就調(diào)用新建的MethodAccessor對象的invoke()方法了。這里需要注意的是:generateMethod方法在生成MethodAccessorImpl對象時(shí),會(huì)在內(nèi)存中生成對應(yīng)的字節(jié)碼,并調(diào)用ClassDefiner.defineClass創(chuàng)建對應(yīng)的 Class 對象,實(shí)現(xiàn)如下:
在ClassDefiner.defineClass方法實(shí)現(xiàn)中,每被調(diào)用一次都會(huì)生成一個(gè)DelegatingClassLoader類加載器對象:
這里每次都生成新的類加載器,是為了性能考慮,在某些情況下可以卸載這些生成的類,因?yàn)轭惖男遁d是只有在類加載器可以被回收的情況下才會(huì)被回收的,如果用了原來的類加載器,那可能導(dǎo)致這些新創(chuàng)建的類一直無法被卸載,從其設(shè)計(jì)來看本身就不希望這些類一直存在內(nèi)存里的,在需要的時(shí)候有就行啦!
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Java反射在實(shí)際工作中的應(yīng)用筆記
- Java編程反射機(jī)制用法入門與實(shí)例總結(jié)
- 通過反射實(shí)現(xiàn)Java下的委托機(jī)制代碼詳解
- java反射獲取一個(gè)object屬性值代碼解析
- Java利用反射獲取object的屬性和值代碼示例
- java反射獲取和調(diào)用方法
- Java反射機(jī)制實(shí)例代碼分享
- JAVA反射機(jī)制中g(shù)etClass和class對比分析
- Java反射之Call stack introspection詳解
- Java反射簡易教程
- Java反射機(jī)制概念、原理與用法總結(jié)
- Java 基礎(chǔ)詳解(泛型、集合、IO、反射)
- Java 反射機(jī)制的實(shí)例詳解
- Java反射中java.beans包學(xué)習(xí)總結(jié)
相關(guān)文章
Spring 重定向(Redirect)指南及相關(guān)策略問題
本文介紹了在Spring中實(shí)現(xiàn)重定向的三種不同方法,在執(zhí)行這些重定向時(shí)如何處理/傳遞屬性以及如何處理HTTP POST請求的重定向。關(guān)于Spring 重定向(Redirect)指南的相關(guān)知識(shí)大家參考下本文2017-11-11通過Mybatis實(shí)現(xiàn)單表內(nèi)一對多的數(shù)據(jù)展示示例代碼
最近做項(xiàng)目遇到這樣的需求要求將表中的數(shù)據(jù),按照一級二級分類返回給前端json數(shù)據(jù),下面通過本文給大家分享通過Mybatis實(shí)現(xiàn)單表內(nèi)一對多的數(shù)據(jù)展示示例代碼,感興趣的朋友參考下吧2017-08-08基于@RequestParam與@RequestBody使用對比
這篇文章主要介紹了@RequestParam與@RequestBody的使用對比,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10Servlet會(huì)話技術(shù)基礎(chǔ)解析
這篇文章主要介紹了Servlet會(huì)話技術(shù)基礎(chǔ)解析,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12java解析dbf之通過javadbf包生成和讀取dbf文件
這篇文章主要介紹了java通過javadbf讀取和生成DBF文件的方法,大家參考使用吧2014-01-01SpringBoot + minio實(shí)現(xiàn)分片上傳、秒傳、續(xù)傳功能
MinIO是一個(gè)基于Go實(shí)現(xiàn)的高性能、兼容S3協(xié)議的對象存儲(chǔ),使用MinIO構(gòu)建用于機(jī)器學(xué)習(xí),分析和應(yīng)用程序數(shù)據(jù)工作負(fù)載的高性能基礎(chǔ)架構(gòu),這篇文章主要介紹了SpringBoot + minio實(shí)現(xiàn)分片上傳、秒傳、續(xù)傳,需要的朋友可以參考下2023-06-06k8s解決java服務(wù)下載超時(shí)問題小結(jié)
我們在走ingress的java程序的時(shí)候,往往會(huì)有導(dǎo)出數(shù)據(jù)的功能,這個(gè)時(shí)候就會(huì)有因網(wǎng)絡(luò)慢、后臺(tái)處理時(shí)間過長導(dǎo)致下載超時(shí),也有因下載文件太大,導(dǎo)致下載失敗,下面給分享k8s解決java服務(wù)下載超時(shí)問題,感興趣的朋友跟隨小編一起看看吧2024-06-06Mybatis讀取和存儲(chǔ)json類型數(shù)據(jù)的實(shí)現(xiàn)
本文主要介紹了Mybatis讀取和存儲(chǔ)json類型數(shù)據(jù)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06