java method 参数名_如何在运行期获取method中的参数名

因为一次在做项目的时候需要扫描接口的信息,其中包括参数名,遇到了点障碍就想着把这个解决方案和问题讲一下。

我们要查看的方法如下

class Facade {

public void a(String a,String b){

int aa = 1;

for (int i = 0; i < 100; i++) {

aa++;

}

}

}

1. Java1.8以后

java1.8以后,官方提供了反射的方法能获取到接口的参数名称。示例如下。其中getParameters方法是1.8才开始提供的。并且需要在javac编译时,加上-parameters参数才行。

Method[] methods = Facade.class.getDeclaredMethods();

for (Method method : methods) {

Parameter[] parameters = method.getParameters();

for (Parameter parameter : parameters) {

System.out.println(parameter.getName());

}

}

通过javap -p -v可以查看class的字节码,如下

Classfile /Users/cey/Worksapce/weidai/gateway-extension/src/test/java/com/weidai/middleware/gateway/Facade.class

Last modified Apr 24, 2019; size 320 bytes

MD5 checksum be01198ac8c5e00915a8cbfc153deaab

Compiled from "DubboDetectorTest.java"

class Facade

minor version: 0

major version: 52

flags: ACC_SUPER

Constant pool:

#1 = Methodref #3.#14 // java/lang/Object."":()V

#2 = Class #15 // Facade

#3 = Class #16 // java/lang/Object

#4 = Utf8

#5 = Utf8 ()V

#6 = Utf8 Code

#7 = Utf8 LineNumberTable

#8 = Utf8 a

#9 = Utf8 (Ljava/lang/String;Ljava/lang/String;)V

#10 = Utf8 MethodParameters

#11 = Utf8 b

#12 = Utf8 SourceFile

#13 = Utf8 DubboDetectorTest.java

#14 = NameAndType #4:#5 // "":()V

#15 = Utf8 Facade

#16 = Utf8 java/lang/Object

{

Facade();

descriptor: ()V

flags:

Code:

stack=1, locals=1, args_size=1

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: return

LineNumberTable:

line 54: 0

public void a(java.lang.String, java.lang.String);

descriptor: (Ljava/lang/String;Ljava/lang/String;)V

flags: ACC_PUBLIC

Code:

stack=0, locals=3, args_size=3

0: return

LineNumberTable:

line 57: 0

MethodParameters:

Name Flags

a

b

}

SourceFile: "DubboDetectorTest.java"

其中MethodParameters就是1.8后在字节码中记录参数名的地方。但是1.8之前是怎么实现的呢?

2.spring是怎么获取到参数名的

spring中有个ParameterNameDiscoverer接口,他有6个实现类。如下:

a7f4336f445c

image (2).png

2.1 Aspect

Aspect开头的都是对增强类的信息获取。我用不到。

2.2 PrioritizedParameterNameDiscoverer

PrioritizedParameterNameDiscoverer是一个链表,就是记录一系列的Discoverer。

2.3 StandardReflectionParameterNameDiscoverer

这个Discoverer就是封装了JDK1.8的getParameters

2.4 LocalVariableTableParameterNameDiscoverer

这个类是重点,它通过asm获取了class文件的LocalVariableTable信息。class,字节码如下:

Classfile /Users/cey/Worksapce/weidai/gateway-extension/target/test-classes/com/weidai/middleware/gateway/Facade.class

Last modified Apr 24, 2019; size 598 bytes

MD5 checksum 044e7b84601a25af47aabeb27a6bf828

Compiled from "DubboDetectorTest.java"

class com.weidai.middleware.gateway.Facade

minor version: 0

major version: 52

flags: ACC_SUPER

Constant pool:

#1 = Methodref #3.#22 // java/lang/Object."":()V

#2 = Class #23 // com/weidai/middleware/gateway/Facade

#3 = Class #24 // java/lang/Object

#4 = Utf8

#5 = Utf8 ()V

#6 = Utf8 Code

#7 = Utf8 LineNumberTable

#8 = Utf8 LocalVariableTable

#9 = Utf8 this

#10 = Utf8 Lcom/weidai/middleware/gateway/Facade;

#11 = Utf8 a

#12 = Utf8 (Ljava/lang/String;Ljava/lang/String;)V

#13 = Utf8 i

#14 = Utf8 I

#15 = Utf8 Ljava/lang/String;

#16 = Utf8 b

#17 = Utf8 aa

#18 = Utf8 StackMapTable

#19 = Utf8 MethodParameters

#20 = Utf8 SourceFile

#21 = Utf8 DubboDetectorTest.java

#22 = NameAndType #4:#5 // "":()V

#23 = Utf8 com/weidai/middleware/gateway/Facade

#24 = Utf8 java/lang/Object

{

com.weidai.middleware.gateway.Facade();

descriptor: ()V

flags:

Code:

stack=1, locals=1, args_size=1

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: return

LineNumberTable:

line 54: 0

LocalVariableTable:

Start Length Slot Name Signature

0 5 0 this Lcom/weidai/middleware/gateway/Facade;

public void a(java.lang.String, java.lang.String);

descriptor: (Ljava/lang/String;Ljava/lang/String;)V

flags: ACC_PUBLIC

Code:

stack=2, locals=5, args_size=3

0: iconst_1

1: istore_3

2: iconst_0

3: istore 4

5: iload 4

7: bipush 100

9: if_icmpge 21

12: iinc 3, 1

15: iinc 4, 1

18: goto 5

21: return

LineNumberTable:

line 56: 0

line 57: 2

line 58: 12

line 57: 15

line 60: 21

LocalVariableTable:

Start Length Slot Name Signature

5 16 4 i I

0 22 0 this Lcom/weidai/middleware/gateway/Facade;

0 22 1 a Ljava/lang/String;

0 22 2 b Ljava/lang/String;

2 20 3 aa I

StackMapTable: number_of_entries = 2

frame_type = 253 /* append */

offset_delta = 5

locals = [ int, int ]

frame_type = 250 /* chop */

offset_delta = 15

MethodParameters:

Name Flags

a

b

}

SourceFile: "DubboDetectorTest.java"

其中有一行字节码记录了LocalVariableTable信息,LocalVariableTable里不仅保存了参数名,还保存了其他局部变量信息。spring通过slot来判定哪些是参数以及参数的顺序。

但是LocalVariableTable不是类的必须信息,所以不是编译后必须存在的。只有在javac时-g或-g:vars时,才会保存LocalVariableTable信息。

在idea工具中,我们可以通过如下方式,关闭编译时,自动生成LocalVariableTable来尝试查看字节码。

a7f4336f445c

image (3).png

5.DefaultParameterNameDiscoverer

这个Discoverer就是在1.8时多添加了个StandardReflectionParameterNameDiscoverer。

6.不是任何时候都能获取到参数名

在ParameterNameDiscoverer接口上有这么段注释:

a7f4336f445c

image (4).png

它告诉我们,不是任何时候都能获取到参数名的,只能尝试去获取。

3.spring mvc可能会获取不到参数名吗

当我们关闭了class debug信息,并且将编译级别设置为1.6时,启动一个简单的spring boot项目。在idea中关闭操作如下:

a7f4336f445c

image (5).png

controller如下:

@RestController

@RequestMapping("/")

public class MainController {

@RequestMapping("/")

public String index(String info){

return info;

}

}

我们会发现这时候访问该接口传递info参数会报如下错误:

java.lang.IllegalArgumentException: Name for argument type [java.lang.String] not available, and parameter name information not found in class file either.

所以,spring mvc中也是有可能获取不到方法参数名的。如果我们需要使用spring mvc的话,最好通过Require等注解来绑定。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值