Java通過反射獲取方法參數名的方式小結
1、前言
一般當我們用類似下面的反射去獲取方法參數名時得到的一般是 arg0、arg1, 參數名默認會丟失, 導致無法獲取到真實的方法參數名,下面介紹如何通過其他方式解決。
class A { void getUser(String userName, String userId){} } Method method = A.class.getMethod(String.class,String.class); Parameter[] parameters = method.getParameters(); for (Parameter parameter : parameters) { String name = parameter.getName(); System.out.println(name); // 得到的是arg0, arg1 而非 userName、userId }
2、解決方式
方式2.1: 添加編譯參數配置 -parameters
java默認在編譯工程代碼為class文件時,會將方法參數名擦除,替換成arg0、arg1之類。但是我們可以在編譯時配置將參數名也一并編譯進去,在maven中新增如下編譯插件配置
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <!-- 配置編譯時將方法參數名保留 --> <compilerArgs> <arg>-parameters</arg> </compilerArgs> </configuration> </plugin>
對于較新的 maven 版本(>= 3.6.2), 也可以直接使用 parameters 配置項:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <parameters>true</parameters> </configuration> </plugin>
添加-parameters
的編譯參數后,可以看到我們編譯后的class文件是源碼的參數名,而非原來的arg0、arg1。 之后我們用原生的反射Parameter.getName()
就可以獲取到的方法參數的真實參數名了
注意: 該方式僅對JDK8及以上版本有效, 以前版本JDK沒有提供該保留機制
方式2.2: 使用Spring的內部工具類 - ParameterNameDiscoverer
如果正好你的項目依賴了spring,則可以直接使用spring的內部工具類ParameterNameDiscoverer
去獲取到方法真實參數名。ParameterNameDiscoverer的實現類 主要包含 LocalVariableTableParameterNameDiscoverer、StandardReflectionParameterNameDiscoverer 。 下面介紹如何使用
1、StandardReflectionParameterNameDiscoverer的使用
該發(fā)現器需要與 方式2.1: 添加編譯參數配置 -parameters 一樣添加編譯配置, 因為底層也是直接用反射Parameter去獲取而已
// 創(chuàng)建參數名發(fā)現器 ParameterNameDiscoverer discoverer = new StandardReflectionParameterNameDiscoverer(); Method method = A.class.getMethod(String.class,String.class); // 獲取方法真實參數名. userName, userId String[] parameterNames = discoverer.getParameterNames(method);
2、LocalVariableTableParameterNameDiscoverer的使用
// 創(chuàng)建參數名發(fā)現器 ParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer(); Method method = A.class.getMethod(String.class,String.class); // 獲取方法真實參數名. userName, userId String[] parameterNames = discoverer.getParameterNames(method);
該發(fā)現器底層原理是在編譯類時生成一個調試信息的局部變量表LocalVariableTable,然后基于ASM字節(jié)碼技術去解析LocalVariableTable得到參數名, 所以它也需要在編譯時額外添加 編譯配置, 請在maven中添加如下-g的編譯參數配置
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <compilerArgs> <!-- 生成局部變量表 --> <arg>-g:vars</arg> </compilerArgs> </configuration> </plugin> </plugins> </build>
該方法優(yōu)點是JDK8以下版本也能使用,但是缺點是無法獲取到接口的方法參數名
3、DefaultParameterNameDiscoverer的使用
// 創(chuàng)建參數名發(fā)現器 ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer(); Method method = A.class.getMethod(String.class,String.class); // 獲取方法真實參數名. userName, userId String[] parameterNames = discoverer.getParameterNames(method);
這個參數名發(fā)現器是一個組合模式的發(fā)現器, 本身不去實現獲取參數名的邏輯,而是組合其他參數名發(fā)現器, 然后迭代調用每個參數名發(fā)現器直到找到真實參數名。 從下面源碼來看 主要內置了前面講到的兩種StandardReflectionParameterNameDiscoverer 和 LocalVariableTableParameterNameDiscoverer。 所以如果要讓對應的參數名發(fā)現器生效,需要配合對應發(fā)現器的使用邏輯 否則一樣無法獲取到方法參數名。
// 源碼 public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer { public DefaultParameterNameDiscoverer() { if (!GraalDetector.inImageCode()) { if (KotlinDetector.isKotlinReflectPresent()) { addDiscoverer(new KotlinReflectionParameterNameDiscoverer()); } addDiscoverer(new StandardReflectionParameterNameDiscoverer()); // 依賴編譯參數 -parameters addDiscoverer(new LocalVariableTableParameterNameDiscoverer()); // // 依賴編譯參數 -g } } }
使用總結
1、參數名獲取的本質
- 所有方案都依賴編譯時保留參數名信息(通過-parameters或-g參數), 所以編譯的時候有就是有,沒有就是沒有,尤其是一些第三方依賴的類
- 如果是第三方依賴的類若未攜帶調試信息(如未使用上述編譯參數進行編譯),則無論如何也是無法獲取真實參數名, 除非第三方庫已使用-parameters編譯,仍可通過反射獲?。ǖ珜嶋H場景較少見)
2、版本適配建議
- JDK8+項目: 優(yōu)先使用
-parameters
編譯參數(性能最佳), 配合StandardReflectionParameterNameDiscoverer
- JDK7及以下: 使用
-g
編譯參數, 配合LocalVariableTableParameterNameDiscoverer
- Spring項目: 推薦使用
DefaultParameterNameDiscoverer
(自動適配最優(yōu)策略), 可同時配置兩種編譯參數提升兼容性:
3、在一些springBoot 2.0以后版本中spring-boot-starter-parent會內置了編譯參數配置,所以不用配也可以直接使用ParameterNameDiscoverer, 當然需要確保項目是否覆蓋了默認配置導致失效,需要重新配
知名應用場景舉例
- spring-mvc 的 @RequestParam 參數名的可省略配置
- mybatis 的 @Param 也可以不用配置
到此這篇關于Java通過反射獲取方法參數名的方式小結的文章就介紹到這了,更多相關Java反射獲取方法參數名內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
在啟動后臺 jar包時,使用指定的 application.yml操作
這篇文章主要介紹了在啟動后臺 jar包時,使用指定的 application.yml操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10Springboot基于assembly的服務化打包方案及spring boot部署方式
這篇文章主要介紹了Springboot基于assembly的服務化打包方案及springboot項目的幾種常見的部署方式,本文主要針對第二種部署方式提供一種更加友好的打包方案,需要的朋友可以參考下2017-12-12使用SpringBoot開發(fā)Restful服務實現增刪改查功能
Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發(fā)過程。這篇文章主要介紹了基于SpringBoot開發(fā)一個Restful服務,實現增刪改查功能,需要的朋友可以參考下2018-01-01