rt. 分享Java获取方法参数定义的名称,什么意思?就是获取方法定义时的参数起名,如 sayHello(String name)的“name”,如果你不写底层,也许找不到这东西会有什么用途,实际上最初在接触到springmvc的时候就有此一问,为什么直接在方法中声明变量就可以获取到参数,不知道它是怎么做到的,当然使用@RequestParam注解就比较好理解了。记得去年在翻看spring-core.jar文件时,找到了LocalVariable的定义,然后瞪着眼睛试了一下,哟呵,竟然是获取方法参数名称定义的实现,于是借着这个理念改版了一下全局的日志记录实现,可转至《新的Spring Aop实现全局日志记录功能》。

回到主题,获取参数定义的实现这里一共提供三种实现,分别是Spring、JDK8、Javassist,通过定义一个类和方法(方法中提供一些参数)来实践期望得到的结果,该方法的类定义如下:

packagecn.chendd.tips.examples.varible;importorg.junit.runner.RunWith;importorg.junit.runners.JUnit4;importjava.awt.*;importjava.lang.reflect.Method;importjava.util.Date;/**
* @authorchendd
*/@RunWith(JUnit4.class)public abstract classBaseTest {protectedMethodmethod=super.getClass().getMethod("sayHello", String.class, Integer.TYPE,
Character.class, Date.class, Point.class, Boolean.TYPE);publicBaseTest()throwsNoSuchMethodException {
}/**
*定义一个函数,包含一些基本类型与包装类型及对象类型*/publicString sayHello(String name,intage, Character sex, Date birthday, Point point ,booleanflag) {return"hello: "+ name;
}
}

Spring

Spring的实现主要是基于ParameterNameDiscoverer接口的实现,更老的版本没去查看,3.X的版本中就有此实现,本次整理了三种实现,分别是:LocalVariableTableParameterNameDiscoverer、DefaultParameterNameDiscoverer及基于Aop实现的获取(见上面文章地址中的实现),其中前两种经过尝试均可以实现且他们都实现该接口,并且最后查看源码发现DefaultParameterNameDiscoverer的实现似乎更为严格,它有涵盖LocalVariable的实现,另外像Standard的标准实现并未正常获取,参考它们的继承实现结构为:

java获取方法的返回类型 java获取方法参数的值_Test

packagecn.chendd.tips.examples.varible;importorg.junit.Assert;importorg.junit.Test;importorg.springframework.core.DefaultParameterNameDiscoverer;importorg.springframework.core.LocalVariableTableParameterNameDiscoverer;/**
*使用Spring core自带的函数实现* @authorchendd
* @date2019-08-08 20:39:20
*/public final classGetDefinitionBySpringextendsBaseTest {publicGetDefinitionBySpring()throwsNoSuchMethodException {super();
}@Testpublic voidtestLocalVariable() {
LocalVariableTableParameterNameDiscoverer localParameter =newLocalVariableTableParameterNameDiscoverer();
String parameterNames[] = localParameter.getParameterNames(method);
Assert.assertEquals(parameterNames.length,method.getParameterCount());for(inti =0; i < parameterNames.length; i++) {
System.out.print(parameterNames[i] +",  ");
}
System.out.println();
}@Testpublic voidtestDefaultVariable() {
DefaultParameterNameDiscoverer defaultParameter =newDefaultParameterNameDiscoverer();
String[] parameterNames = defaultParameter.getParameterNames(method);
Assert.assertEquals(parameterNames.length,method.getParameterCount());for(inti =0; i < parameterNames.length; i++) {
System.out.print(parameterNames[i] +",  ");
}
System.out.println();
}
}

运行结果

java获取方法的返回类型 java获取方法参数的值_java获取方法的返回类型_02

JDK8

JDK8的新特性中有一项是编译字节码的时候按照源码定义进行编译,在此之前均被编译成arg0、arg1这种,现在可以在编译的时候通过增加编译参数使其编译的class文件支持按源码定义编译,再配合反射进行获取就可以了,当然它必须要求JDK8及其以上版本,如果你觉得无法接受还需去设置IDE的编译参数,而又实用maven之类的项目构建,也可以给maven编译时增加这项参数,这样就无须在检出项目的时候进行参数设置了,IDE以IDEA为例,修改设置如下:

java获取方法的返回类型 java获取方法参数的值_System_03

maven项目编译增加

java获取方法的返回类型 java获取方法参数的值_System_04

参考代码

packagecn.chendd.tips.examples.varible;importorg.junit.Test;importjava.lang.reflect.Parameter;/**
*使用JDK8新特性实现* @authorchendd
* @date2019-08-08 21:39:20
*/public classGetDefinitionByJDK8extendsBaseTest {publicGetDefinitionByJDK8()throwsNoSuchMethodException {
}@Testpublic voidtestJDKVariable() {
Parameter[] parameters =method.getParameters();for(Parameter parameter : parameters) {
System.out.println(parameter);
}
}
}

运行结果

java获取方法的返回类型 java获取方法参数的值_System_05

Javassit

Javassit是一个很高大上的动态编程技术,比如可以帮你想实现类似JavaScript的eval函数的功能)我猜Apache Commons JCI应该也可以),这里重点至少拿它去打个蚊子,实现一个获取函数中的参数定义名称的功能,参考代码如下:

packagecn.chendd.tips.examples.varible;importjavassist.*;importjavassist.bytecode.*;importorg.junit.Test;/**
*使用Javassit实现* @authorchendd
* @date2019-08-09 22:39:20
*/public classGetDefinitionByJavassistextendsBaseTest {publicGetDefinitionByJavassist()throwsNoSuchMethodException {
}@Testpublic voidtestJavassistVariable()throwsNotFoundException {
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.getCtClass(super.getClass().getName());
CtMethod ctMethods[] = ctClass.getMethods();
String methodName ="sayHello";for(CtMethod ctMethod : ctMethods) {if(! methodName.equals(ctMethod.getName())){continue;
}
MethodInfo methodInfo = ctMethod.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attribute = (LocalVariableAttribute) codeAttribute.getAttribute("LocalVariableTable");//LocalVariableTableintlens = attribute.tableLength();for(inti =1; i < lens; i++) {
System.out.print(attribute.variableName(i) +",\t");
}
}
}
}

运行结果就不提供了,跟你预料的一样。至此提供了三种方式的获取方法的参数定义名称,简单总结一下:

(1)如果使用Spring则可以考虑第一种方法,第一种方法个人觉得Default的实现就好;

(2)如果使用JDK8及其以上版本,并不介意修改了IDE编译器设置或者直接使用maven构建项目使用这种方法也挺好,直接正常反射就行;

(3)如果有更加高大上的动态编程可以考虑使用Javassist;

源码地址