|
| 1 | +## 通过反射获得方法的参数信息 |
| 2 | +JDK8之前 .class文件是不会存储方法参数信息的,因此也就无法通过反射获取该信息(想想反射获取类信息的入口是什么?当然就是Class类了)。即是是在JDK11里 |
| 3 | +也不会默认生成这些信息,可以通过在javac加上-parameters参数来让javac生成这些信息(javac就是java编译器,可以把java文件编译成.class文件)。生成额外 |
| 4 | +的信息(运行时非必须信息)会消耗内存并且有可能公布敏感信息(某些方法参数比如password,JDK文档里这么说的),并且确实很多信息javac并不会为我们生成,比如 |
| 5 | +LocalVariableTable,javac就不会默认生成,需要你加上 -g:vars来强制让编译器生成,同样的,方法参数信息也需要加上 |
| 6 | +-parameters来让javac为你在.class文件中生成这些信息,否则运行时反射是无法获取到这些信息的。在讲解Java语言层面的方法之前,先看一下javac加上该 |
| 7 | +参数和不加生成的信息有什么区别(不感兴趣想直接看运行代码的可以跳过这段)。下面是随便写的一个类。 |
| 8 | +```java |
| 9 | +public class ByteCodeParameters { |
| 10 | + public String simpleMethod(String canUGetMyName, Object yesICan) { |
| 11 | + return "9527"; |
| 12 | + } |
| 13 | +} |
| 14 | +``` |
| 15 | +先来不加参数编译和反编译一下这个类javac ByteCodeParameters.java , javap -v ByteCodeParameters: |
| 16 | +```java |
| 17 | + //只截取了部分信息 |
| 18 | + public java.lang.String simpleMethod(java.lang.String, java.lang.Object); |
| 19 | + descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String; |
| 20 | + flags: (0x0001) ACC_PUBLIC |
| 21 | + Code: |
| 22 | + stack=1, locals=3, args_size=3 |
| 23 | + 0: ldc #2 // String 9527 |
| 24 | + 2: areturn |
| 25 | + LineNumberTable: |
| 26 | + line 5: 0 |
| 27 | + //这个方法的描述到这里就结束了 |
| 28 | +``` |
| 29 | +接下来我们加上参数javac -parameters ByteCodeParameters.java 再来看反编译的信息: |
| 30 | +```java |
| 31 | + public java.lang.String simpleMethod(java.lang.String, java.lang.Object); |
| 32 | + descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String; |
| 33 | + flags: (0x0001) ACC_PUBLIC |
| 34 | + Code: |
| 35 | + stack=1, locals=3, args_size=3 |
| 36 | + 0: ldc #2 // String 9527 |
| 37 | + 2: areturn |
| 38 | + LineNumberTable: |
| 39 | + line 8: 0 |
| 40 | + MethodParameters: |
| 41 | + Name Flags |
| 42 | + canUGetMyName |
| 43 | + yesICan |
| 44 | +``` |
| 45 | +可以看到.class文件里多了一个MethodParameters信息,这就是参数的名字,可以看到默认是不保存的。 |
| 46 | +<br>下面看一下在Intelj Idea里运行的这个例子,我们试一下通过反射获取方法名 : |
| 47 | +```java |
| 48 | +public class ByteCodeParameters { |
| 49 | + public String simpleMethod(String canUGetMyName, Object yesICan) { |
| 50 | + return "9527"; |
| 51 | + } |
| 52 | + |
| 53 | + public static void main(String[] args) throws NoSuchMethodException { |
| 54 | + Class<?> clazz = ByteCodeParameters.class; |
| 55 | + Method simple = clazz.getDeclaredMethod("simpleMethod", String.class, Object.class); |
| 56 | + Parameter[] parameters = simple.getParameters(); |
| 57 | + for (Parameter p : parameters) { |
| 58 | + System.out.println(p.getName()); |
| 59 | + } |
| 60 | + } |
| 61 | +} |
| 62 | +输出 : |
| 63 | +arg0 |
| 64 | +arg1 |
| 65 | +``` |
| 66 | +???说好的方法名呢????别急,哈哈。前面说了,默认是不生成参数名信息的,因此我们需要做一些配置,我们找到IDEA的settings里的Java Compiler选项,在 |
| 67 | +Additional command line parameters:一行加上-parameters(Eclipse 也是找到Java Compiler选中Stoer information about method parameters),或者自 |
| 68 | +己编译一个.class文件放在IDEA的out下,然后再来运行 : |
| 69 | +```java |
| 70 | +输出 : |
| 71 | +canUGetMyName |
| 72 | +yesICan |
| 73 | +``` |
| 74 | +这样我们就通过反射获取到参数信息了。想要了解更多的同学可以自己研究一下 [官方文档] |
| 75 | +(https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html) |
| 76 | +<br> |
| 77 | +## 总结与补充 |
| 78 | +在JDK8之后,可以通过-parameters参数来让编译器生成参数信息然后在运行时通过反射获取方法参数信息,其实在SpringFramework |
| 79 | +里面也有一个LocalVariableTableParameterNameDiscoverer对象可以获取方法参数名信息,有兴趣的同学可以自行百度(这个类在打印日志时可能会比较有用吧,个人感觉)。 |
0 commit comments