小技巧分享之3种方式获取Java方法参数名称
小技巧admin 发布于:2019-08-10 19:05:30
阅读:loading
rt. 分享Java获取方法参数定义的名称,什么意思?就是获取方法定义时的参数起名,如 sayHello(String name)的“name”,如果你不写底层,也许找不到这东西会有什么用途,实际上最初在接触到springmvc的时候就有此一问,为什么直接在方法中声明变量就可以获取到参数,不知道它是怎么做到的,当然使用@RequestParam注解就比较好理解了。记得去年在翻看spring-core.jar文件时,找到了LocalVariable的定义,然后瞪着眼睛试了一下,哟呵,竟然是获取方法参数名称定义的实现,于是借着这个理念改版了一下全局的日志记录实现,可转至《新的Spring Aop实现全局日志记录功能》。
回到主题,获取参数定义的实现这里一共提供三种实现,分别是Spring、JDK8、Javassist,通过定义一个类和方法(方法中提供一些参数)来实践期望得到的结果,该方法的类定义如下:
package cn.chendd.tips.examples.varible;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.awt.*;
import java.lang.reflect.Method;
import java.util.Date;
/**
* @author chendd
*/
@RunWith(JUnit4.class)
public abstract class BaseTest {
protected Method method = super.getClass().getMethod("sayHello", String.class, Integer.TYPE,
Character.class, Date.class, Point.class , Boolean.TYPE);
public BaseTest() throws NoSuchMethodException {
}
/**
* 定义一个函数,包含一些基本类型与包装类型及对象类型
*/
public String sayHello(String name, int age, Character sex, Date birthday, Point point , boolean flag) {
return "hello: " + name;
}
}
Spring的实现主要是基于ParameterNameDiscoverer接口的实现,更老的版本没去查看,3.X的版本中就有此实现,本次整理了三种实现,分别是:LocalVariableTableParameterNameDiscoverer、DefaultParameterNameDiscoverer及基于Aop实现的获取(见上面文章地址中的实现),其中前两种经过尝试均可以实现且他们都实现该接口,并且最后查看源码发现DefaultParameterNameDiscoverer的实现似乎更为严格,它有涵盖LocalVariable的实现,另外像Standard的标准实现并未正常获取,参考它们的继承实现结构为:
package cn.chendd.tips.examples.varible;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
/**
* 使用Spring core自带的函数实现
* @author chendd
* @date 2019-08-08 20:39:20
*/
public final class GetDefinitionBySpring extends BaseTest {
public GetDefinitionBySpring() throws NoSuchMethodException {
super();
}
@Test
public void testLocalVariable() {
LocalVariableTableParameterNameDiscoverer localParameter = new LocalVariableTableParameterNameDiscoverer();
String parameterNames[] = localParameter.getParameterNames(method);
Assert.assertEquals(parameterNames.length, method.getParameterCount());
for (int i = 0; i < parameterNames.length; i++) {
System.out.print(parameterNames[i] + ", ");
}
System.out.println();
}
@Test
public void testDefaultVariable() {
DefaultParameterNameDiscoverer defaultParameter = new DefaultParameterNameDiscoverer();
String[] parameterNames = defaultParameter.getParameterNames(method);
Assert.assertEquals(parameterNames.length, method.getParameterCount());
for (int i = 0; i < parameterNames.length; i++) {
System.out.print(parameterNames[i] + ", ");
}
System.out.println();
}
}
JDK8的新特性中有一项是编译字节码的时候按照源码定义进行编译,在此之前均被编译成arg0、arg1这种,现在可以在编译的时候通过增加编译参数使其编译的class文件支持按源码定义编译,再配合反射进行获取就可以了,当然它必须要求JDK8及其以上版本,如果你觉得无法接受还需去设置IDE的编译参数,而又实用maven之类的项目构建,也可以给maven编译时增加这项参数,这样就无须在检出项目的时候进行参数设置了,IDE以IDEA为例,修改设置如下:
package cn.chendd.tips.examples.varible;
import org.junit.Test;
import java.lang.reflect.Parameter;
/**
* 使用JDK8新特性实现
* @author chendd
* @date 2019-08-08 21:39:20
*/
public class GetDefinitionByJDK8 extends BaseTest {
public GetDefinitionByJDK8() throws NoSuchMethodException {
}
@Test
public void testJDKVariable() {
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter);
}
}
}
Javassit是一个很高大上的动态编程技术,比如可以帮你想实现类似JavaScript的eval函数的功能)我猜Apache Commons JCI应该也可以),这里重点至少拿它去打个蚊子,实现一个获取函数中的参数定义名称的功能,参考代码如下:
package cn.chendd.tips.examples.varible;
import javassist.*;
import javassist.bytecode.*;
import org.junit.Test;
/**
* 使用Javassit实现
* @author chendd
* @date 2019-08-09 22:39:20
*/
public class GetDefinitionByJavassist extends BaseTest {
public GetDefinitionByJavassist() throws NoSuchMethodException {
}
@Test
public void testJavassistVariable() throws NotFoundException {
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");//LocalVariableTable
int lens = attribute.tableLength();
for (int i = 1; i < lens; i++) {
System.out.print(attribute.variableName(i) + ",\t");
}
}
}
}
运行结果就不提供了,跟你预料的一样。至此提供了三种方式的获取方法的参数定义名称,简单总结一下:
(1)如果使用Spring则可以考虑第一种方法,第一种方法个人觉得Default的实现就好;
(2)如果使用JDK8及其以上版本,并不介意修改了IDE编译器设置或者直接使用maven构建项目使用这种方法也挺好,直接正常反射就行;
(3)如果有更加高大上的动态编程可以考虑使用Javassist;
https://gitee.com/88911006/chendd-examples 见 tips 模块。
点赞