目錄
* 反射操作方法 <https://www.cnblogs.com/zhangxinhua/p/11543653.html#反射操作方法>
* Spring的方法的優(yōu)點(diǎn)
<https://www.cnblogs.com/zhangxinhua/p/11543653.html#spring的方法的優(yōu)點(diǎn)>
* 反射如何實(shí)現(xiàn)Spring的方法
<https://www.cnblogs.com/zhangxinhua/p/11543653.html#反射如何實(shí)現(xiàn)spring的方法>
* Java字節(jié)碼 <https://www.cnblogs.com/zhangxinhua/p/11543653.html#java字節(jié)碼>
* 高級(jí)反射注意點(diǎn) <https://www.cnblogs.com/zhangxinhua/p/11543653.html#高級(jí)反射注意點(diǎn)>
* javac的彩蛋 <https://www.cnblogs.com/zhangxinhua/p/11543653.html#javac的彩蛋>
* 續(xù)點(diǎn) <https://www.cnblogs.com/zhangxinhua/p/11543653.html#續(xù)點(diǎn)>
* 每日一笑 <https://www.cnblogs.com/zhangxinhua/p/11543653.html#每日一笑>
* 上期答案 <https://www.cnblogs.com/zhangxinhua/p/11543653.html#上期答案>
* # 加入戰(zhàn)隊(duì) <https://www.cnblogs.com/zhangxinhua/p/11543653.html#加入戰(zhàn)隊(duì)>
* 微信公眾號(hào) <https://www.cnblogs.com/zhangxinhua/p/11543653.html#微信公眾號(hào)>
之前我們已經(jīng)介紹了Java中框架常用的技術(shù)---反射。可以這么說(shuō)反射方便了我們的開發(fā)。今天我們來(lái)說(shuō)說(shuō)他的短板,或者說(shuō)我們今天在反射的基礎(chǔ)上在進(jìn)行方便化。
反射操作方法
在上一章節(jié)中我們學(xué)會(huì)了通過(guò)反射去調(diào)用方法。
public class App { public void test(String str, Integer integer) {
System.out.println(str); System.out.println(integer); } }
這個(gè)時(shí)候如果我想獲取test方法對(duì)象的話應(yīng)該這么做
Method testMethod = App.class.getMethod("test", String.class, Integer.class);
這里就不在贅述如何通過(guò)Method對(duì)象調(diào)用方法了。文章末尾會(huì)給出上一章節(jié)的地址。今天我們要研究的是Method如何獲取方法參數(shù)這一塊??此坪?jiǎn)單卻又是那么的傳奇。我們看看下面一段代碼執(zhí)行的效果
public static void main(String[] args) throws ParseException,
NoSuchMethodException { Method[] methods = App.class.getMethods(); Method
testMethod = App.class.getMethod("test", String.class, Integer.class);
Class<?>[] parameterTypes = testMethod.getParameterTypes(); Parameter[]
parameters = testMethod.getParameters(); for (Parameter parameter : parameters)
{ System.out.println(parameter.getName()); } }
那么輸出的兩個(gè)參數(shù)名稱是什么呢?一開始筆者這里想當(dāng)然的認(rèn)為是 str , name 。 相信此時(shí)的你應(yīng)該和我一樣認(rèn)為是str , name 。
對(duì)的,你沒(méi)看錯(cuò)返回的居然是無(wú)意義的名稱 , arg0 , arg1.這就奇怪了。至于為什么呢?我現(xiàn)在還不想告訴你。下面會(huì)慢慢告訴你。
Spring的方法的優(yōu)點(diǎn)
做過(guò)Javaweb開發(fā)的肯定都用過(guò)spring,springmvc ,
在寫controller層的時(shí)候我們都會(huì)在方法里直接寫key值的名稱,然后在請(qǐng)求地址中給相應(yīng)的key賦值。
@RequestMapping(value = "/deptId", method = RequestMethod.GET) public
PagedResult<SysDept> selectSysDeptsByPK(Integer pageNumber, Integer pageSize) {
return sysDeptService.selectSysDeptsByPK(deptId, pageNumber, pageSize); }
上述的controller我們?cè)谇岸税l(fā)送請(qǐng)求后會(huì)這樣發(fā)送
http://{ip}:{port}/{projectName}/deptId?pageNumber=1&pageSize=5
這里我問(wèn)一下你們有沒(méi)有想過(guò)為什么springmvc它能夠通過(guò)你傳遞的參數(shù)一一進(jìn)行對(duì)應(yīng)呢?我們上面已經(jīng)嘗試過(guò)通過(guò)反射是無(wú)法獲取方法參數(shù)名稱的。而springmvc無(wú)非就是反射操作方法的。這里是不是很神奇,不得不佩服springmvc的強(qiáng)大。強(qiáng)大到讓人害怕。
反射如何實(shí)現(xiàn)Spring的方法
上面兩個(gè)案例揭露了反射的缺點(diǎn)以及springmvc的強(qiáng)大。這里需要借助springmvc提供的一個(gè)工具ParameterNameDiscoverer 。
這個(gè)類顧名思義就是發(fā)現(xiàn)參數(shù)名稱。在使用這個(gè)類之前我們先來(lái)了解下為什么反射獲取不到方法名稱。
這里需要簡(jiǎn)單說(shuō)說(shuō)Java執(zhí)行過(guò)程,Java之所以可以跨容器是因?yàn)镴ava針對(duì)各個(gè)系統(tǒng)提供了不同jvm,所以我們開發(fā)前都需要安裝不同版本的jdk,jdk里面提供了jvm,java
代碼運(yùn)行期間是通過(guò)jvm去操作class文件的。但是我們平時(shí)都是開發(fā)java文件的。所以在jvm執(zhí)行之前會(huì)有一個(gè)編譯期間。javac就是用來(lái)變異java文件為class文件的。
package com.zxhtom.test; /** * Hello world! */ public class App { public void
test(String str, Integer integer) { } }
針對(duì)上述代碼我們通過(guò)javac進(jìn)行編譯下試試看看效果。
javac App.java
編譯完成之后會(huì)出現(xiàn)一個(gè)同名的class文件
然后我們?cè)谕ㄟ^(guò)命令查看下這個(gè)class文件
javap -verbose App.class
通過(guò)查看App.java對(duì)應(yīng)的字節(jié)碼發(fā)現(xiàn)在javac編譯的時(shí)候?qū)τ诜椒ǖ拿Q根本不會(huì)去記錄的。想想也對(duì)我執(zhí)行方法的時(shí)候只需要按順序?qū)?shù)放進(jìn)去就行了。根本不需要關(guān)心參數(shù)名稱是什么。那么問(wèn)題顯而易見(jiàn)了jvm不需要參數(shù)名所以編譯時(shí)過(guò)率了。但是我們反射想通過(guò)參數(shù)名稱一一對(duì)應(yīng)這樣效率更快。那么是springmvc是如何解決的呢。
private static final ParameterNameDiscoverer parameterNameDiscoverer = new
LocalVariableTableParameterNameDiscoverer(); public static void main(String[]
args) throws ParseException, NoSuchMethodException { java.lang.reflect.Method
testMethod = App.class.getMethod("test", String.class, Integer.class); String[]
parameterNames = parameterNameDiscoverer.getParameterNames(testMethod); for
(String parameterName : parameterNames) { System.out.println(parameterName); } }
對(duì),就是ParameterNameDiscoverer這個(gè)方法幫助了我們。這里簡(jiǎn)單說(shuō)說(shuō)ParameterNameDiscoverer
作用。springmvc中會(huì)有一個(gè)默認(rèn)的ParameterNameDiscoverer解釋器DefaultParameterNameDiscoverer該類繼承
PrioritizedParameterNameDiscoverer。PrioritizedParameterNameDiscoverer
這個(gè)類就是getParameterNames去獲取方法名的。在springmvc中通過(guò)addDiscoverer方法有三個(gè)類注冊(cè)到
PrioritizedParameterNameDiscoverer
* KotlinReflectionParameterNameDiscoverer : Spring5.0提供 ,但是也得jdk8及以上版本使用
* StandardReflectionParameterNameDiscoverer : Spring4.0提供 ,但是也得jdk8及以上版本使用
* LocalVariableTableParameterNameDiscoverer
:Spring2.0就有了,對(duì)JDK版本沒(méi)啥要求,完全Spring自己實(shí)現(xiàn)的獲取字段名稱,邏輯復(fù)雜些,效率稍微低一點(diǎn)
總結(jié)一下就是在springmvc4.0之前springmvc都是通過(guò)自己實(shí)現(xiàn)的一套代碼去獲取字節(jié)碼然后分析的。這里能力有限就不分析了。
在4.0以后因?yàn)镴ava8的推出彌補(bǔ)了這個(gè)bug.springmvc也就都采用jdk提供的功能獲取參數(shù)名了。下面我們來(lái)看看jdk8是如何解決這個(gè)問(wèn)題的。
Java字節(jié)碼
在上一節(jié)我們通過(guò)javac ,
javap命令進(jìn)行了Java的編譯了查看。我們發(fā)現(xiàn)class字節(jié)碼中記錄的信息有【常量區(qū),類,方法】其中對(duì)于代碼的記錄有位置,堆,棧,行號(hào)等等。這也是我們jvm調(diào)優(yōu)的依據(jù)。但是這僅僅是我們使用簡(jiǎn)單的javac的編譯。
javac -g : 編譯更加全面點(diǎn)
經(jīng)過(guò)對(duì)比發(fā)現(xiàn)javac 和javac -g 的區(qū)別好像是javac -g 編譯信息多出LocalVariableTable信息。
名稱 解釋
LineNumberTable 屬性表存放方法的行號(hào)信息
LocalVariableTable 屬性表中存放方法的局部變量信息
上圖中通過(guò)javac -g
編譯的信息中LocalVarableTable有三條數(shù)據(jù),是因?yàn)樵诰幾g期間每個(gè)非靜態(tài)方法第一個(gè)參數(shù)都是this.去除第一條剩下的其實(shí)就是我們需要的參數(shù)信息。但是我們這個(gè)時(shí)候去執(zhí)行一下看看效果。
高級(jí)反射注意點(diǎn)
所謂的高級(jí)反射其實(shí)就是對(duì)jdk版本的要求,只要是jdk8的版本,就可以用jdk提供的parameter方法獲取參數(shù)名了。在編譯的時(shí)候需要加上
-parameters
javac的彩蛋
續(xù)點(diǎn)
每日一笑
今天公司放假,和老婆商量好一起去她家,出發(fā)前夕,老婆說(shuō):給我家里人的禮物買好了么?然后我去臥室全搬出來(lái)了;給她說(shuō):這是你舅的,這是你叔的,這是你爺爺?shù)?,這是你奶奶的,這是你爸的,這是你媽的,這是…………然后我倆就打起來(lái)了!
上期答案
無(wú)
加入戰(zhàn)隊(duì) <https://www.cnblogs.com/zhangxinhua/p/11543653.html#addMe>
# 加入戰(zhàn)隊(duì)
微信公眾號(hào)
熱門工具 換一換
