您好,登錄后才能下訂單哦!
今天小編給大家分享一下java怎么通過反射獲得方法真實(shí)參數(shù)名的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
咱們先來看這樣一個(gè)小的demo:
這是一個(gè)很簡(jiǎn)單的小demo,里面就是一個(gè)簡(jiǎn)簡(jiǎn)單單的類Test1
,Test1
有一個(gè)包含兩個(gè)參數(shù)的方法test
,在Test1
的main
方法中通過射來獲得test
方法的所有參數(shù)的名字,并將其輸出到標(biāo)準(zhǔn)流。我本以為這個(gè)demo的運(yùn)行結(jié)果會(huì)得到方法的參數(shù)名。
驚不驚喜,意不意外?和說好的不一樣?。?/p>
咱們先停一下,先把為什么反射沒有拿到正確的值放到一邊,先說說我為什么要研究“通過反射原理獲得方法參數(shù)的實(shí)際名稱”這件事呢:是因?yàn)槲蚁敕抡詹?shí)現(xiàn)Spring MVC中的“自動(dòng)綁定”功能。大家知道Spring MVC里有一個(gè)“自動(dòng)綁定”的功能,能夠自動(dòng)綁定請(qǐng)求參數(shù)的值到@RequestMapping
方法的參數(shù)上的,而不用任何額外的操作。
這個(gè)功能我覺得很方便,所以我想嘗試自己仿造這個(gè)功能,然后用在公司的項(xiàng)目開發(fā)中。我猜測(cè)Spring是通過反射獲得方法的參數(shù)名后根據(jù)參數(shù)名到request
中getParam(String name)
來獲得實(shí)際的值然后綁定的。因此我就嘗試著按照這個(gè)思路做,結(jié)果就遇到了上邊提到的反射獲得不了參數(shù)實(shí)際名稱的問題。我將這個(gè)問題請(qǐng)教了老大,老大了解到我的意圖后,經(jīng)過驗(yàn)證,得出結(jié)論:Spring MVC能不能正常使用自動(dòng)綁定是與java編譯器編譯時(shí)加不加-g
參數(shù)有關(guān)的,而這個(gè)-g
參數(shù)是代表著java編譯器在編譯時(shí)是否會(huì)輸出調(diào)試信息。
其實(shí)也就是說:Spring是通過讀取java編譯器生成的調(diào)試信息從而獲得的方法中參數(shù)的真實(shí)名稱的。說到這里,這個(gè)問題基本也解決了,但是我還是想再多說一點(diǎn)我后續(xù)的學(xué)習(xí)結(jié)果。后續(xù)我研究了一下Spring對(duì)于方法參數(shù)這塊的處理邏輯,也就是對(duì)于“自動(dòng)綁定”功能的底層的實(shí)現(xiàn)。
那么,Spring 到底是用了什么“黑科技”來做到獲得方法實(shí)際參數(shù)名的呢,咱們不妨就看Spring的源碼吧,看看Spring到底是如何實(shí)現(xiàn)的。
Spring海量的源代碼,從何看起呢,這里,我是這樣解決的:我大體知道這個(gè)獲得方法實(shí)際參數(shù)名的操作應(yīng)當(dāng)和Method
的getParameters()
方法有關(guān),或者說它的方法里或許會(huì)調(diào)用到這個(gè)方法,那么好了,我們可以使用idea
提供的“查看調(diào)用?!钡墓δ?,來順藤摸瓜,看看在Spring中有沒有調(diào)用到這個(gè)方法,如果有,那么解決方案應(yīng)當(dāng)就在調(diào)用方法的附近。
我們可以看到,果不其然,在調(diào)用棧里就有org.spring
包中的方法,其中有兩個(gè)方法都是StandardReflectionParameterNameDiscoverer
類的方法,其實(shí)我們已經(jīng)找到了,看這個(gè)類的名字就能知道,它是處理ParameterName
的Discoverer的(在這里我想再說點(diǎn)題外話,我個(gè)人非常贊同Spring這種全命名的編碼風(fēng)格,看到命名就能看明白這個(gè)類是在干什么,所以說代碼應(yīng)當(dāng)是能“自描述”的)
好,我們?cè)倩氐酱a中來,繼續(xù)看這個(gè)類:發(fā)現(xiàn)它有一段簡(jiǎn)要的注釋:
大意就是這個(gè)類是針對(duì)使用了JDK8基于-parameters
編譯參數(shù)的ParameterNameDiscoverer
的實(shí)現(xiàn),這里這個(gè)-parameters
參數(shù)是怎么回事咱們先放一邊。
繼續(xù)向上看StandardReflectionParameterNameDiscoverer
所實(shí)現(xiàn)的這個(gè)接口ParameterNameDiscoverer
,打開ParameterNameDiscoverer
這個(gè)接口,我們用idea的查看子類的功能,能夠看到它一共有包括StandardReflectionParameterNameDiscoverer
在內(nèi)的8
個(gè)子類
其中有一個(gè)名字里帶“Default”的子類DefaultParameterNameDiscoverer
,按照一般套路來說,帶Default的都是默認(rèn)的實(shí)現(xiàn),那么好了我們優(yōu)先看它吧。
打開DefaultParameterNameDiscoverer
,我們發(fā)現(xiàn),他做的大體就是通過判斷standardReflectionAvailable
這個(gè)值來走向不同分支流程:一個(gè)是走向剛才提到的利用JDK8編譯參數(shù)的StandardReflectionParameterNameDiscoverer
另一個(gè)是走向了LocalVariableTableParameterNameDiscoverer
好,現(xiàn)在又出現(xiàn)了熟悉的StandardReflectionParameterNameDiscoverer
了,那么我們返回去看吧,一會(huì)再看另一個(gè)分支的LocalVariableTableParameterNameDiscoverer
。
我們回到StandardReflectionParameterNameDiscoverer
中,再來看剛才那個(gè)-parameters
編譯參數(shù),這是個(gè)什么黑科技?既然他是個(gè)編譯參數(shù),那么咱們不妨試著用它編譯一下咱們的代碼試一下吧。
我們將idea
設(shè)置上-parameters
編譯參數(shù)從新運(yùn)行剛才的demo,發(fā)現(xiàn)這回的輸出結(jié)果是:
已經(jīng)能夠拿到參數(shù)的真實(shí)名稱了。那么,這個(gè)-parameters
到底是什么呢:我們可以來看一下oracle官方提供的javac文檔:
通過文檔可以看出加上這個(gè)參數(shù)后,編譯器會(huì)生成元數(shù)據(jù),從而使方法參數(shù)的反射能夠拿到參數(shù)的信息。
這個(gè)功能是jdk8的新特性,我們就不仔細(xì)展開了,詳情可以查看這兩篇文檔:
JDK 8 Features
JEP 118: Access to Parameter Names at Runtime
-parameters
這個(gè)黑科技咱們已經(jīng)了解了,利用這個(gè)編譯參數(shù)是可以獲得方法參數(shù)的真實(shí)名稱的,但是這個(gè)參數(shù)是jdk8之后才有的,那么之前的版本如何獲得呢?我們繼續(xù)看Spring源代碼吧?,F(xiàn)在我們來看另一個(gè)分支:LocalVariableTableParameterNameDiscoverer
,打開這個(gè)類:
其實(shí)看注釋就明白了,這個(gè)LocalVariableTableParameterNameDiscoverer
是通過ASM library
分析LocalVariableTable
來實(shí)現(xiàn)獲得參數(shù)實(shí)際名稱的,ASM
是一個(gè)第三方的字節(jié)碼操縱庫,用這個(gè)庫可以讀取寫入class文件,這個(gè)庫有很廣泛的應(yīng)用,具體的我不展開介紹了。
我們重點(diǎn)說一下這個(gè)LocalVariableTable
吧,這個(gè)LocalVariableTable
是什么呢?我們不用文字來說明了,直接來看代碼吧:
我們這次不看源文件了,來直接看編譯后的class文件。
用idea
打開Test1.class
:
然后在View
菜單中點(diǎn)選Show Bytecode
:
在彈出窗口中,我們可以看到,idea
以大綱的方式把class
文件的信息列了出來,而在其中就有LocalVariableTable
存在,而且在“LocalVariableTable”附近我們可以看到我們定義方法的參數(shù)的真實(shí)名稱。現(xiàn)在我們也就明白了,對(duì)于8以下的jdk編譯環(huán)境,Spring是使用ASM來讀取class
文件中LocalVariableTable
信息從而獲得參數(shù)真實(shí)名稱的。
到此為止,我們已經(jīng)基本了解了Spring中自動(dòng)綁定背后的黑科技了。
這里我還想繼續(xù)再多說一點(diǎn),有關(guān)LocalVariableTable
和Java class
文件:class
文件可以說是Java實(shí)現(xiàn)跨平臺(tái)特性的根本!不管在什么平臺(tái)下,只要編譯出來的class
文件符合規(guī)范,虛擬機(jī)就能夠正常的執(zhí)行。了解一下class
文件的相關(guān)知識(shí)其實(shí)對(duì)于理解各類class
文件操縱庫以及基于class
操縱的AOP
等等編程模式的原理是很有幫助的,所以我們可以了解一下class
文件是什么樣的結(jié)構(gòu)的。想要了解class
文件的結(jié)構(gòu),最權(quán)威的莫過于官方的《Java虛擬機(jī)規(guī)范了》,在Java虛擬機(jī)規(guī)范中,第四章是有關(guān)class
文件結(jié)構(gòu)的內(nèi)容,我們可以大致過一遍。
通過閱讀,我們可以大致了解到class
的結(jié)構(gòu):
A class file consists of a stream of 8-bit bytes. All 16-bit, 32-bit, and 64-bit
quantities are constructed by reading in two, four, and eight consecutive 8-bit
bytes, respectively. Multibyte data items are always stored in big-endian order,
where the high bytes come first. In the Java SE platform, this format is supported
by interfaces java.io.DataInput and java.io.DataOutput and classes such as
java.io.DataInputStream and java.io.DataOutputStream.
class文件可以用一個(gè)結(jié)構(gòu)來表示:
這個(gè)結(jié)構(gòu)中每一項(xiàng)大致的含義我們來簡(jiǎn)單說明一下吧(詳情請(qǐng)查看虛擬機(jī)規(guī)范):
開頭的magic
u4
叫做“魔數(shù)”,Java虛擬器通過讀取這個(gè)數(shù)來判斷當(dāng)前文件是不是有效的u4
代表它是無符號(hào)
的4
個(gè)byte
,這個(gè)數(shù)始終應(yīng)該是0xCAFEBABE
;
minor_version
、major_version
分別是class
文件的次版本
和主版本
;
u2
constant_pool_count
、cp_info
constant_pool[constant_pool_count-1]
代表常量池中項(xiàng)目數(shù)和代表了常量池本身;
u2
access_flags
: 代表class
訪問標(biāo)記,例如:public protected;
u2
this_class
: 代表放置類名在常量池中的索引;
u2
super_class
: 代表父類名稱在常量池中的索引;
u2
interfaces_count
; u2
interfaces[interfaces_count]
; 代表所實(shí)現(xiàn)的接口集合的大小,及接口集合本身;
u2
fields_count
; field_info
fields[fields_count]
; 代表屬性集合大小以及屬性集合本身;
u2
methods_count
; method_info
methods[methods_count]
; 代表方法集合大小以及方法集合本身;
u2
attributes_count
; attribute_info
attributes[attributes_count]
; java class
文件內(nèi)部屬性信息集合大小和內(nèi)部屬性信息集合本身。這里提一下,我們前面的提到的LocalVariableTable
的信息就存儲(chǔ)在這里。
以上就是“java怎么通過反射獲得方法真實(shí)參數(shù)名”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。