maven-paranamer-plugin

What is it?

It is a library that allows the parameter names of non-private methods and constructors to be accessed at runtime. Normally this information is dropped by the compiler. In effect, methods like doSometing(mypkg.Person toMe) currently look like doSomething(mypackage.Person ???) to people using Java’s reflection to inspect methods.

项目主页:https://github.com/paul-hammant/paranamer

如何在运行时获取java方法参数名称?

  1. 由于jdk的反射方式,未提供获取参数名的方法
  2. Java的字节码文件默认不存储参数名称。在使用javac编译时,如果开启-g:{vars}选项,可以增加Local variable debugging information。对java方法,参数实际是按照局部变量来存储的,所以可以获取参数名称;但对于java接口中的方法声明,这种方法就无法获取参数名称。

Paranamer专门用来解决获取参数名的问题。其原理是在编译阶段,修改.class文件,在类或接口的字节码文件中增加一个字符串常量,这个常量保存了所有的方法声明信息,包括方法名、参数类型、参数名称。这样在运行时,class loader加载类文件以后,使用Paranamer的api去读取这个字符串,就可以获取参数名称。加上java反射,实际上可以把源代码重现出来。

这个工具可以用来实现代码生成器。其缺点在于,它需要修改源项目的build步骤。据其项目主页说明,Java 8已经加入存储参数名称的功能。

1
2
3
4
5
6
7
8
9
10
// MySomethingOrOther.java**
Method method = Foo.class.getMethod(...);
Paranamer paranamer = new CachingParanamer();
String[] parameterNames = paranamer.lookupParameterNames(method) // throws ParameterNamesNotFoundException if not found
// or:
parameterNames = paranamer.lookupParameterNames(method, false) // will return null if not found

踩过的坑

需求:通过java的反射获取method,使用paranamer获取method的参数名称。
诡异之处:使用Paranamer paranamer = new CachingParanamer()l.ookupParameterNames(method);无法正常获取方法的变量名称。

一个简单的java类,请注意使用了import .* 的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package hbec.commons.services.stock.controller;
import hbec.commons.domain.stock.condition.dto.*;
import hbec.commons.domain.stock.vo.*;
import hbec.exception.portal.HbecPortalStockBizException;
import hbec.platform.commons.annotations.HbecService;
import hbec.platform.commons.exceptions.HbecDbServiceException;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* @author roc Jul 4, 2016
*
*/
public interface IConditionTradeController {
ConditionSaveResult save(Integer userId, @NotNull ConditionTradeDto conditionTradeDto) ;
ConditionSaveResult saveTurnPoint(Integer userId, @NotNull ConditionTradeTurnPointDto conditionTradeDto) ;
ConditionSaveResult saveBatching(Integer userId, @NotNull ConditionTradeBatchingDto conditionTradeDto) ;
ConditionSaveResult saveGrid(Integer userId, @NotNull GridTradeDto conditionTradeDto) ;
ConditionSaveResult saveProfit(Integer userId, ProfitConditionTradeDto profitConditionTradeDto) ;
Integer delete(Integer userId, @NotNull Integer warnId) ;
Integer pause(Integer userId, @NotNull Integer warnId) ;
Integer resume(Integer userId, @NotNull Integer warnId) ;
PageVo<ConditionTradeMarketVO> findMonitorPageWithMarket(Integer userId, String code, ConditionPageVo<ConditionTradeMarketVO> pager) ;
ConditionTradeDetailVo detail(@NotNull Integer userId, @NotNull Integer warnId) ;
PageVo<ConditionTradeHistoryVo> queryHistory(@NotNull Integer userId, String stockCode, PageVo<ConditionTradeHistoryVo> pager) ;
ConditionTradeDetailVo detailWithMarket(Integer userId, Integer warnId) ;
List<EntrustVO> queryEntrustRecord(Integer userId, Integer warnId) ;
List<ConditionInfoVO> queryEntrustResult(Integer userId, String stockCode, String historyIds) ;
PageCursorVo<ConditionInfoVO> queryEntrustedResult(Integer userId, String stockCode, Integer warnId, Integer cursor, Integer pageSize) ;
CountTradeHistoryVo countEntrustedResult(Integer userId, String stockCode, Integer warnId) ;
int markAsRead(Integer userId, String stockCode, Integer warnId) ;
}

使用paranamer-maven-plugin编译之后如下:

1
public static final String __PARANAMER_DATA = "countConditionNum java.lang.Integer userId \ncountEntrustedResult java.lang.Integer,java.lang.String,java.lang.Integer userId,stockCode,warnId \ndelete java.lang.Integer,java.lang.Integer userId,warnId \ndetail java.lang.Integer,java.lang.Integer userId,warnId \ndetailWithMarket java.lang.Integer,java.lang.Integer userId,warnId \nentrustManual java.lang.Integer,java.lang.Integer,EntrustDto userId,warnId,entrustDto \nfindCurrentEntrust java.lang.Integer,java.lang.String,PageVo userId,stockCode,pager \nfindMonitorPageWithMarket java.lang.Integer,java.lang.String,ConditionPageVo userId,code,pager \nfindPage java.lang.Integer,java.lang.String,ConditionPageVo userId,strategyState,pager \nfindTriggeredPage java.lang.Integer,java.lang.Integer,PageVo userId,handled,pager \nmarkAsRead java.lang.Integer,java.lang.String,java.lang.Integer userId,stockCode,warnId \npause java.lang.Integer,java.lang.Integer userId,warnId \nqueryEntrustRecord java.lang.Integer,java.lang.Integer userId,warnId \nqueryEntrustResult java.lang.Integer,java.lang.String,java.lang.String userId,stockCode,historyIds \nqueryEntrustedResult java.lang.Integer,java.lang.String,java.lang.Integer,java.lang.Integer,java.lang.Integer userId,stockCode,warnId,cursor,pageSize \nqueryHistory java.lang.Integer,java.lang.String,PageVo userId,stockCode,pager \nresume java.lang.Integer,java.lang.Integer userId,warnId \nsave java.lang.Integer,ConditionTradeDto userId,conditionTradeDto \nsaveBatching java.lang.Integer,ConditionTradeBatchingDto userId,conditionTradeDto \nsaveGrid java.lang.Integer,GridTradeDto userId,conditionTradeDto \nsaveNewShares java.lang.Integer,NewSharesDto userId,newSharesDto \nsaveProfit java.lang.Integer,ProfitConditionTradeDto userId,profitConditionTradeDto \nsaveTurnPoint java.lang.Integer,ConditionTradeTurnPointDto userId,conditionTradeDto \nsaveVipBatching java.lang.Integer,ConditionTradeBatchingDto userId,conditionTradeDto \n";

仔细观察可以发现生成的__PARANAMER_DATA常量中,并未包含对象类型变量的具体package路径,此时使用new CachingParanamer()l.ookupParameterNames(method)无法正常获取方法的变量名称。

去掉import .* 的方式之后,再次编译,常量中包含了对象类型变量具体的package路径,此时可成功获取方法的变量名称。

1
public static final String __PARANAMER_DATA = "countConditionNum java.lang.Integer userId \ncountEntrustedResult java.lang.Integer,java.lang.String,java.lang.Integer userId,stockCode,warnId \ndelete java.lang.Integer,java.lang.Integer userId,warnId \ndetail java.lang.Integer,java.lang.Integer userId,warnId \ndetailWithMarket java.lang.Integer,java.lang.Integer userId,warnId \nentrustManual java.lang.Integer,java.lang.Integer,hbec.commons.domain.stock.dto.EntrustDto userId,warnId,entrustDto \nfindCurrentEntrust java.lang.Integer,java.lang.String,hbec.commons.domain.stock.vo.PageVo userId,stockCode,pager \nfindMonitorPageWithMarket java.lang.Integer,java.lang.String,hbec.commons.domain.stock.vo.ConditionPageVo userId,code,pager \nfindPage java.lang.Integer,java.lang.String,hbec.commons.domain.stock.vo.ConditionPageVo userId,strategyState,pager \nfindTriggeredPage java.lang.Integer,java.lang.Integer,hbec.commons.domain.stock.vo.PageVo userId,handled,pager \nmarkAsRead java.lang.Integer,java.lang.String,java.lang.Integer userId,stockCode,warnId \npause java.lang.Integer,java.lang.Integer userId,warnId \nqueryEntrustRecord java.lang.Integer,java.lang.Integer userId,warnId \nqueryEntrustResult java.lang.Integer,java.lang.String,java.lang.String userId,stockCode,historyIds \nqueryEntrustedResult java.lang.Integer,java.lang.String,java.lang.Integer,java.lang.Integer,java.lang.Integer userId,stockCode,warnId,cursor,pageSize \nqueryHistory java.lang.Integer,java.lang.String,hbec.commons.domain.stock.vo.PageVo userId,stockCode,pager \nresume java.lang.Integer,java.lang.Integer userId,warnId \nsave java.lang.Integer,hbec.commons.domain.stock.dto.ConditionTradeDto userId,conditionTradeDto \nsaveBatching java.lang.Integer,hbec.commons.domain.stock.dto.ConditionTradeBatchingDto userId,conditionTradeDto \nsaveGrid java.lang.Integer,hbec.commons.domain.stock.dto.GridTradeDto userId,conditionTradeDto \nsaveNewShares java.lang.Integer,hbec.commons.domain.stock.dto.NewSharesDto userId,newSharesDto \nsaveProfit java.lang.Integer,hbec.commons.domain.stock.condition.dto.ProfitConditionTradeDto userId,profitConditionTradeDto \nsaveTurnPoint java.lang.Integer,hbec.commons.domain.stock.dto.ConditionTradeTurnPointDto userId,conditionTradeDto \nsaveVipBatching java.lang.Integer,hbec.commons.domain.stock.dto.ConditionTradeBatchingDto userId,conditionTradeDto \n";