如何通过反射获得方法的真实参数名(以及扩展研究)

前段时间 , 在做一个小的工程时 , 遇到了需要通过反射获得方法真实参数名的场景 , 在这里我遇到了一些小小的问题 , 后来在部门老大的指导下 , 我解决了这个问题 。 通过解决这个问题 , 附带着我了解到了很多新的知识 , 我觉得有必要和大家分享交流一下 。
咱们先来看这样一个小的demo:
如何通过反射获得方法的真实参数名(以及扩展研究)文章插图
这是一个很简单的小demo , 里面就是一个简简单单的类Test1 , Test1有一个包含两个参数的方法test , 在Test1的main方法中通过射来获得test方法的所有参数的名字 , 并将其输出到标准流 。 我本以为这个demo的运行结果会得到方法的参数名 , 结果:
如何通过反射获得方法的真实参数名(以及扩展研究)文章插图
惊不惊喜 , 意不意外?和说好的不一样啊!
咱们先停一下 , 先把为什么反射没有拿到正确的值放到一边 , 先说说我为什么要研究“通过反射原理获得方法参数的实际名称”这件事呢:是因为我想仿照并实现Spring MVC中的“自动绑定”功能 。 大家知道Spring MVC里有一个“自动绑定”的功能 , 能够自动绑定请求参数的值到@RequestMapping方法的参数上的 , 而不用任何额外的操作 。
如何通过反射获得方法的真实参数名(以及扩展研究)文章插图
这个功能我觉得很方便 , 所以我想尝试自己仿造这个功能 , 然后用在公司的项目开发中 。 我猜测Spring是通过反射获得方法的参数名后根据参数名到request中getParam(String name)来获得实际的值然后绑定的 。 因此我就尝试着按照这个思路做 , 结果就遇到了上边提到的反射获得不了参数实际名称的问题 。 我将这个问题请教了老大 , 老大了解到我的意图后 , 经过验证 , 得出结论:Spring MVC能不能正常使用自动绑定是与Java编译器编译时加不加-g参数有关的 , 而这个-g参数是代表着java编译器在编译时是否会输出调试信息 。
如何通过反射获得方法的真实参数名(以及扩展研究)文章插图
其实也就是说:Spring是通过读取java编译器生成的调试信息从而获得的方法中参数的真实名称的 。 说到这里 , 这个问题基本也解决了 , 但是我还是想再多说一点我后续的学习结果 。 后续我研究了一下Spring对于方法参数这块的处理逻辑 , 也就是对于“自动绑定”功能的底层的实现 。
那么 , Spring 到底是用了什么“黑科技”来做到获得方法实际参数名的呢 , 咱们不妨就看Spring的源码吧 , 看看Spring到底是如何实现的 。
Spring海量的源代码 , 从何看起呢 , 这里 , 我是这样解决的:我大体知道这个获得方法实际参数名的操作应当和Method的getParameters()方法有关 , 或者说它的方法里或许会调用到这个方法 , 那么好了 , 我们可以使用idea提供的“查看调用栈”的功能 , 来顺藤摸瓜 , 看看在Spring中有没有调用到这个方法 , 如果有 , 那么解决方案应当就在调用方法的附近 。
如何通过反射获得方法的真实参数名(以及扩展研究)文章插图
我们可以看到 , 果不其然 , 在调用栈里就有org.spring包中的方法 , 其中有两个方法都是StandardReflectionParameterNameDiscoverer类的方法 , 其实我们已经找到了 , 看这个类的名字就能知道 , 它是处理ParameterName的Discoverer的(在这里我想再说点题外话 , 我个人非常赞同Spring这种全命名的编码风格 , 看到命名就能看明白这个类是在干什么 , 所以说代码应当是能“自描述”的)
如何通过反射获得方法的真实参数名(以及扩展研究)文章插图
如何通过反射获得方法的真实参数名(以及扩展研究)文章插图
好 , 我们再回到代码中来 , 继续看这个类:发现它有一段简要的注释:
如何通过反射获得方法的真实参数名(以及扩展研究)文章插图
大意就是这个类是针对使用了JDK8基于-parameters编译参数的ParameterNameDiscoverer的实现 , 这里这个-parameters参数是怎么回事咱们先放一边 , 继续向上看StandardReflectionParameterNameDiscoverer所实现的这个接口ParameterNameDiscoverer , 打开ParameterNameDiscoverer这个接口 , 我们用idea的查看子类的功能 , 能够看到它一共有包括StandardReflectionParameterNameDiscoverer在内的8个子类
如何通过反射获得方法的真实参数名(以及扩展研究)文章插图
其中有一个名字里带“Default”的子类DefaultParameterNameDiscoverer , 按照一般套路来说 , 带Default的都是默认的实现 , 那么好了我们优先看它吧 。