默认情况下,我们无法获取方法中的参数名称。通过反射机制只能得到参数的顺序和一些无意义的变量:arg0、arg1等。但这部分信息我们确实需要。比如IDE的自动提示,文档化的服务接口的详细信息等等。这是因为这些变量的名字根本就没有编译到class文件中,不可能凭空生成。JDK8之后,可以在编译时指定-parameters选项,将方法的参数名记录到class文件中,在运行时通过反射机制获取相关信息。如果你的项目是用maven构建的,你可以添加几行配置和添加参数。maven-compiler-plugin3.8.01.81.8utf8-parameters如果使用IDEA等编辑器,也可以通过设置界面进行配置。但是,不建议这样做,因为您的配置不容易共享。在一个普通的Java项目中,可以通过以下方式获取反射数据。新增Method.getParameters方法。publicclassTest{publicstaticvoidmain(String[]args)throwsException{Classclazz=Class.forName("com.test.MethodParameterTest");方法[]methods=clazz.getMethods();Constructor[]constructors=clazz.getConstructors();for(构造函数constructor:constructors){System.out.println("+++"+constructor.getName());Parameter[]parameters=constructor.getParameters();for(Parameterparameter:parameters){printParameter(parameter);}}System.out.println("----------------");for(Methodmethod:methods){System.out.println(method.getName());Parameter[]parameters=method.getParameters();for(Parameterparameter:parameters){printParameter(parameter);}}}privatestaticvoidprintParameter(Parameterparameter){//参数名System.out.println("\t\t"+parameter.getName()));//是否在源码中隐式声明了参数名System.out.println("\t\t\timplicit:"+parameter.isImplicit());//在class文件中,是否有参数nameSystem.out.println("\t\t\tnamePresent:"+parameter.isNamePresent());//是不是虚拟参数系统.out.println("\t\t\tsynthetic:"+parameter.isSynthetic());System.out.println("\t\t\tVarArgs:"+parameter.isVarArgs());}}这里有一个few这个方法的含义:isImplicit()参数是否在源文件中被隐式声明,比如一个内部类,默认构造函数(无参数)实际上会使用包含它的主类引用作为第一个参数。编译成一个类,这个参数是一个隐式声明。如果为真,则说明类文件中存在JDK编译器隐式生成的方法参数,但在源文件中不可见。对于常规的普通方法,此值为false。isNamePresent()这个参数在类文件中是否有这个参数名;以编译时是否指定“-parameter”为准。对于指定该参数的编译文件,通常为真;对于JDK内部类和默认编译类,通常为false;这时你会发现它们的参数名通常是表意名称:arg0、arg1等,此时为false。isSynthetic()是否为“虚构”参数,如果为真,则表示它既不是“显式”声明,也不是源文件中的隐式声明,例如“values()”和“valueOf(String)”枚举类”这是编译器“编造”的系统方法。在Spring环境下,由于工具类的支持,会更加方便。publicclassSpringTest{privatestaticfinalParameterNameDiscovererparameterNameDiscoverer=newDefaultParameterNameDiscoverer();publicstaticvoidmain(String[]args)throwsException{Classclazz=Class.forName("com.test.MethodParameterTest");Method[]methods=clazz.getMethods();for(Method)method:System.out.println(method.getName());//JDK1.8+isbetter.String[]parameterNames=parameterNameDiscoverer.getParameterNames(method);if(parameterNames==null){continue;}for(Stringpn:parameterNames){System.out.println("\t\t"+pn);}}}}Java版本低于1.8时,如何获取?我们可以参考Spring的LocalVariableTableParameterNameDiscoverer类。publicString[]getParameterNames(Methodmethod){MethodoriginalMethod=BridgeMethodResolver.findBridgedMethod(方法);returndoGetParameterNames(originalMethod);}@NullableprivateString[]doGetParameterNames(Executableexecutable){Class>declaringClass=executable.getDeclaringClass();Mapmap=this.parameterNamesCache.computeIfAbsent(declaringClass,this::inspectClass);return(map!=NO_DEBUG_INFO_MAP?map.get(executable):null);}最后就走到了inspectClass方法中。privateMapinspectClass(Class>clazz){InputStreamis=clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz));if(is==null){//我们无法加载类文件,这不是致命的//简单地意味着这种发现参数名称的方法不会运行。if(logger.isDebugEnabled()){logger.debug(“无法找到类的'.class'文件[”+clazz+“]-无法确定构造函数/方法参数名称”);}returnNO_DEBUG_INFO_MAP;}try{ClassReaderclassReader=newClassReader(is);Mapmap=newConcurrentHashMap<>(32);classReader.accept(newParameterNameDiscoveringVisitor(clazz,map),0);returnmap;}...可以看到,本例中Spring直接读取class文件被解析。其实就是读取LocalVariableTable中的数据得到的。如果在编译时不添加这些调试选项,同样无法获取方法参数的具体名称。综上所述,在Java8之前,要读取Class中的LocalVariableTable属性表,需要在编译时加上参数-g或-g:vars来获取方法局部变量调试信息;Java8及以后,可以使用java.lang.reflect.Parameter#getName获取,但是编译时需要加上parameter-parameters参数。