[TOC]前端
以前咱們已經介紹了Java中框架經常使用的技術---反射。能夠這麼說反射方便了咱們的開發。今天咱們來講說他的短板,或者說咱們今天在反射的基礎上在進行方便化。java
在上一章節中咱們學會了經過反射去調用方法。web
public class App {
public void test(String str, Integer integer) {
System.out.println(str);
System.out.println(integer);
}
}
複製代碼
這個時候若是我想獲取test方法對象的話應該這麼作spring
Method testMethod = App.class.getMethod("test", String.class, Integer.class);
複製代碼
這裏就不在贅述如何經過Method對象調用方法了。文章末尾會給出上一章節的地址。今天咱們要研究的是Method如何獲取方法參數這一塊。看似簡單卻又是那麼的傳奇。咱們看看下面一段代碼執行的效果bash
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());
}
}
複製代碼
那麼輸出的兩個參數名稱是什麼呢?一開始筆者這裏想固然的認爲是 str , name 。 相信此時的你應該和我同樣認爲是str , name 。微信
對的,你沒看錯返回的竟然是無心義的名稱 , arg0 , arg1.這就奇怪了。至於爲何呢?我如今還不想告訴你。下面會慢慢告訴你。mvc
作過Javaweb開發的確定都用過spring,springmvc , 在寫controller層的時候咱們都會在方法裏直接寫key值的名稱,而後在請求地址中給相應的key賦值。app
@RequestMapping(value = "/deptId", method = RequestMethod.GET)
public PagedResult<SysDept> selectSysDeptsByPK(Integer pageNumber, Integer pageSize) {
return sysDeptService.selectSysDeptsByPK(deptId, pageNumber, pageSize);
}
複製代碼
上述的controller咱們在前端發送請求後會這樣發送 http://{ip}:{port}/{projectName}/deptId?pageNumber=1&pageSize=5框架
這裏我問一下大家有沒有想過爲何springmvc它可以經過你傳遞的參數一一進行對應呢?咱們上面已經嘗試過經過反射是沒法獲取方法參數名稱的。而springmvc無非就是反射操做方法的。這裏是否是很神奇,不得不佩服springmvc的強大。強大到讓人懼怕。jvm
上面兩個案例揭露了反射的缺點以及springmvc的強大。這裏須要藉助springmvc提供的一個工具ParameterNameDiscoverer
。 這個類顧名思義就是發現參數名稱。在使用這個類以前咱們先來了解下爲何反射獲取不到方法名稱。
這裏須要簡單說說Java執行過程,Java之因此能夠跨容器是由於Java針對各個系統提供了不一樣jvm,因此咱們開發前都須要安裝不一樣版本的jdk,jdk裏面提供了jvm,java 代碼運行期間是經過jvm去操做class文件的。可是咱們平時都是開發java文件的。因此在jvm執行以前會有一個編譯期間。javac就是用來變異java文件爲class文件的。
package com.zxhtom.test;
/**
* Hello world!
*/
public class App {
public void test(String str, Integer integer) {
}
}
複製代碼
針對上述代碼咱們經過javac進行編譯下試試看看效果。 javac App.java
編譯完成以後會出現一個同名的class文件
而後咱們在經過命令查看下這個class文件 javap -verbose App.class
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);
}
}
複製代碼
對,就是
ParameterNameDiscoverer
這個方法幫助了咱們。這裏簡單說說
ParameterNameDiscoverer
做用。springmvc中會有一個默認的
ParameterNameDiscoverer
解釋器
DefaultParameterNameDiscoverer
該類繼承
PrioritizedParameterNameDiscoverer
。
PrioritizedParameterNameDiscoverer
這個類就是getParameterNames去獲取方法名的。在springmvc中經過
addDiscoverer
方法有三個類註冊到
PrioritizedParameterNameDiscoverer
總結一下就是在springmvc4.0以前springmvc都是經過本身實現的一套代碼去獲取字節碼而後分析的。這裏能力有限就不分析了。 在4.0之後由於Java8的推出彌補了這個bug.springmvc也就都採用jdk提供的功能獲取參數名了。下面咱們來看看jdk8是如何解決這個問題的。
在上一節咱們經過javac , javap命令進行了Java的編譯了查看。咱們發現class字節碼中記錄的信息有【常量區,類,方法】其中對於代碼的記錄有位置,堆,棧,行號等等。這也是咱們jvm調優的依據。可是這僅僅是咱們使用簡單的javac的編譯。
javac -g : 編譯更加全面點
通過對比發現javac 和javac -g 的區別好像是javac -g 編譯信息多出LocalVariableTable信息。名稱 | 解釋 |
---|---|
LineNumberTable | 屬性表存放方法的行號信息 |
LocalVariableTable | 屬性表中存放方法的局部變量信息 |
上圖中經過javac -g 編譯的信息中LocalVarableTable有三條數據,是由於在編譯期間每一個非靜態方法第一個參數都是this.去除第一條剩下的其實就是咱們須要的參數信息。可是咱們這個時候去執行一下看看效果。
所謂的高級反射其實就是對jdk版本的要求,只要是jdk8的版本,就能夠用jdk提供的parameter方法獲取參數名了。在編譯的時候須要加上 -parameters
## 每日一笑
今天公司放假,和老婆商量好一塊兒去她家,出發前夕,老婆說:給我家裏人的禮物買好了麼?而後我去臥室全搬出來了;給她說:這是你舅的,這是你叔的,這是你爺爺的,這是你奶奶的,這是你爸的,這是你媽的,這是…………而後我倆就打起來了!
無