前言
本篇博文更像是一个工具文章,在阅读Spring源码的时候,经常会遇见一些处理器、提供器之类的组件,有的时候不深入去理解它的含义,确实还读不下去了。
为了方便自己流畅的阅读下去,特开本文记录一些关键的,使用的Spring提供的处理组件,尽量的解释清楚它们的作用甚至原理,以便我们能更自由的阅读,甚至运为己用~
ParameterNameDiscoverer:获取方法参数名称的工具
DefaultParameterNameDiscoverer:它其实就是个聚合的作用:
// Spring4.0后出现的类(伴随着StandardReflectionParameterNameDiscoverer一起出现的) public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer { private static final boolean kotlinPresent = ClassUtils.isPresent("kotlin.Unit", DefaultParameterNameDiscoverer.class.getClassLoader()); public DefaultParameterNameDiscoverer() { // 这里非常非常需要注意的一点是:用于存储的是一个LinkedList(见父类:PrioritizedParameterNameDiscoverer) // LinkedList是先进先出的。所以for循环遍历的时候,会最先执行Kotlin、Standard、Local... 按照这个优先级 if (kotlinPresent) { addDiscoverer(new KotlinReflectionParameterNameDiscoverer()); } addDiscoverer(new StandardReflectionParameterNameDiscoverer()); addDiscoverer(new LocalVariableTableParameterNameDiscoverer()); } }
这里面就是给注册进去了实际意义上干事情的三个ParameterNameDiscoverer。然后它自己的方法getParameterNames和getParameterNames都由注册进来的们去完成~~~~~~~方法都在它的父类:
PrioritizedParameterNameDiscoverer
// Spring2.0就出现了。它的一个原则很简单:这么多发现器,按照顺序执行,随获取到了就算谁的 public class PrioritizedParameterNameDiscoverer implements ParameterNameDiscoverer { private final List<ParameterNameDiscoverer> parameterNameDiscoverers = new LinkedList<>(); public void addDiscoverer(ParameterNameDiscoverer pnd) { this.parameterNameDiscoverers.add(pnd); } @Override @Nullable public String[] getParameterNames(Method method) { for (ParameterNameDiscoverer pnd : this.parameterNameDiscoverers) { String[] result = pnd.getParameterNames(method); if (result != null) { return result; } } return null; } @Override @Nullable public String[] getParameterNames(Constructor<?> ctor) { for (ParameterNameDiscoverer pnd : this.parameterNameDiscoverers) { String[] result = pnd.getParameterNames(ctor); if (result != null) { return result; } } return null; } }
那么接下来说说:StandardReflectionParameterNameDiscoverer和LocalVariableTableParameterNameDiscoverer
StandardReflectionParameterNameDiscoverer:
Spring4.0提供 ,但是也得jdk8及以上版本使用。
Java.lang.reflect 包中提供了很多方法,获取所有的方法,获取所有的参数类型等,但是却没有一个方法能够帮助我们获取方法的参数名列表。JDK提供了方法弥补了这个缺陷
public class StandardReflectionParameterNameDiscoverer implements ParameterNameDiscoverer { @Override @Nullable public String[] getParameterNames(Method method) { return getParameterNames(method.getParameters()); } @Override @Nullable public String[] getParameterNames(Constructor<?> ctor) { return getParameterNames(ctor.getParameters()); } // 因为Parameter这个类是JDK8以上才提供的 @Nullable private String[] getParameterNames(Parameter[] parameters) { String[] parameterNames = new String[parameters.length]; for (int i = 0; i < parameters.length; i++) { Parameter param = parameters[i]; if (!param.isNamePresent()) { return null; } parameterNames[i] = param.getName(); } return parameterNames; } }
LocalVariableTableParameterNameDiscoverer:
Spring2.0就有了,对JDK版本没啥要求,完全Spring自己实现的获取字段名称,逻辑复杂些,效率稍微低一点。
原理是:通过ASM提供的通过字节码获取方法的参数名称,Spring给我们集成了这个提供了这个类,我们只需要简单的使用即可。
备注:这个处理在我们Controller方法入参自动映射时,我们发现我们并不需要写@RequestParam("aaa")这样的注解,也能给自动映射上值,其实底层就运用了它去把这个key分析出来,然后去request里面拿的~
下面看一个Demo吧:
public class Main { private static final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); public void testArguments(String test, Integer myInteger, boolean booleanTest) { } public void test() { } public static void main(String[] args) { Method[] methods = Main.class.getDeclaredMethods(); for (Method method : methods) { String[] parameterNames = parameterNameDiscoverer.getParameterNames(method); System.out.println("方法:" + method.getName() + " 参数为:" + Arrays.asList(parameterNames)); } } }
输出为:
方法:main 参数为:[args] 方法:test 参数为:[] 方法:testArguments 参数为:[test, myInteger, booleanTest]
接下来,我们什么都不改,只改一句话:
private static final ParameterNameDiscoverer parameterNameDiscoverer = new StandardReflectionParameterNameDiscoverer();
运行看看效果:也能除数一样的效果
方法:main 参数为:[args] 方法:test 参数为:[] 方法:testArguments 参数为:[test, myInteger, booleanTest]
这里面需要特别注意了,StandardReflectionParameterNameDiscoverer的使用条件有两个
1、比如是JDK8以上版本
2、必须编译的时候有带上参数:javac -parameters
那么有小伙伴就疑问了,为何我上面编译没加参数也好使呢?这里面小伙伴务必注意了:如果你使用的idea的IDE,它里面默认编辑就带有此参数的:(Compiler-Java Compiler)
若你把它去掉,你的结果会如下:(建议各位小伙伴都试试)
// 去掉这个参数后,获取到的parameterNames 就是null了 // 再次提示一下:若还没效果,请手动重新编译对应文件 String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
好了,讲解完这个,讲视线继续移步到上面的自动注入吧。
为了获取最好的兼容性,推荐使用DefaultParameterNameDiscoverer,而不是直接使用某个具体的Discoverer