先看下面的例子:
package org.reflect;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class MethodName {
public String hello(String hehe,String haha){
return "world";
}
public static void main(String[] args) {
Class<MethodName> clz = MethodName.class;
Method[] methods = clz.getMethods();
for(int i=0;i<methods.length;i++){
Method method = methods[i];
if(method.getName().equals("hello")){
Parameter[] parameters = method.getParameters();
//通过debug看到的名称是arg0和arg1。
}
}
}
}
首先需要清楚的认识到,反射不是万能的,不是任何信息都可以获取到的。在上面的代码中,参数名“hehe”和"haha"就不能简单的通过反射获取到。
(但是不是一定无法通过反射获取到,需要编译设置,请参考:https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html)
那么在编译器没有配置的情况下,怎样获取到参数名称呢。
调查时,使用javap命令看到如下信息:
javap -v -l -s -p -c .class文件路径
public java.lang.String hello(java.lang.String, java.lang.String);
descriptor: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=1, locals=3, args_size=3
0: ldc #2 // String world
2: areturn
LineNumberTable:
line 11: 0
LocalVariableTable:
Start Length Slot Name Signature
0 3 0 this Lorg/reflect/MethodName;
0 3 1 hehe Ljava/lang/String;
0 3 2 haha Ljava/lang/String;
注意LocalVariableTable这个属性,这个属性下面包含参数名称hehe和haha。也就是说这给程序运行时提供了获取参数名称的方法,其实spring也是使用这种方法获取到的(可以参考LocalVariableTableParameterNameDiscoverer这个spring类调查)。
但是这个属性也不是运行时获取的,它是通过读取.class文件获取到的属性。即如果给程序传递一个Class变量,如org.MyClass,那么程序就会读取org/MyClass.class这个文件,从文件中取得LocalVariableTable这个属性的信息,进而获取到参数名称。
(注意:.class文件包含的信息量和Class变量包含的信息量是不同的,虽然Class变量是由加载.class文件得到的,但是并不代表JVM一定把.class文件里的信息全部加载到内存中或者运行时中)。
注意:
上面提到的两种方法,包括spring,都不是一定能获取到的,如果获取不到,就需要手动在注解里,如@RequestParam里指定映射参数。
参照ParameterNameDiscoverer的javadoc。