天天看点

如何获取Java/Groovy中 method 参数的名称?起因Java 中获取 method 参数名的方法Grails / Groovy 中的情况尝试从 MetaMethod 读取参数信息Grails data-binding 代码分析结论

起因

最近在开发一个 Grails 的 Swagger(OpenAPI v3)插件,需要自动从action方法中生成文档,这就需要读取方法的参数名。但是在 Java 中如果编译时没有指定选项则无法获取到方法名,得到的是 argsN 这样的名字。但是 Grails 中的 data-binding 功能却能根据 action 的名称将请求参数对应到同名的参数变量上,这就说明是有办法做到的,至少在 Groovy 中是可以做到的,因此我们可以从阅读、研究 Grails 的 databinding 代码来寻找方法。当然,我已经在网上搜索过了,并没有找到合适的答案。

Java 中获取 method 参数名的方法

  1. 编译时添加 -parameters 选项
  2. 用下面代码获取方法名
Parameter[] parameters = method.getParameters();
if(!parameter.isNamePresent()) {
   throw new IllegalArgumentException("Parameter names are not present!");
}
String parameterName = parameter.getName();
           

参考 SO 文章。

Grails / Groovy 中的情况

在调试 Groovy / Grails 程序时发现 debug 模式下,Method 的 parameters 属性数组长度是有值的,但在非debug模式下,则是0,没有参数信息。

尝试从 MetaMethod 读取参数信息

按照 http://docs.groovy-lang.org/latest/html/api/groovy/lang/MetaMethod.html 文档用 Groovy 的 MetaClass.methods 获取方法对象 MetaMethod 后,发现无法取到参数的名称,只能取到类型。此方法不行。

Grails data-binding 代码分析

原来 Grails 用 ControllerActionTransformer 类,对 controller 的action 添加了额外的代码,来实现“将请求参数名对应到 action 参数名”的功能。

生成用请求参数赋值给 action 方法参数的 transformer 代码是 org.grails.compiler.web.ControllerActionTransformer#initializeMethodParameter。

阅读 grails-plugin-controllers-4.0.3-sources.jar!/org/grails/compiler/web/ControllerActionTransformer.java:377 这里,可以看到加了哪些逻辑。

如何获取Java/Groovy中 method 参数的名称?起因Java 中获取 method 参数名的方法Grails / Groovy 中的情况尝试从 MetaMethod 读取参数信息Grails data-binding 代码分析结论
如何获取Java/Groovy中 method 参数的名称?起因Java 中获取 method 参数名的方法Grails / Groovy 中的情况尝试从 MetaMethod 读取参数信息Grails data-binding 代码分析结论

由此可见,Grails 能将方法参数名与请求参数名对应,这个功能的实现是依靠 Groovy Compiler Transformer 在编译阶段动态添加了一些代码来实现的,而不是依靠注解、类信息来实现的。

因此,我们要想获得参数名这个信息,也许也要依靠 Groovy Transformer 来收集一些信息,然后自动生成一个 @ApiDocInternal annotation,这样就能取到需要的参数信息了。【感觉此方法可行!】

注意同一个注解 Java 只允许在同一个地方(方法、类)出现一次。

并且看 org.grails.compiler.web.ControllerActionTransformer#convertToMethodAction() 的代码,grails 会创建一个无参数的action同名方法,来调用原 action 方法,这就是为什么非 debug 模式下,用 java 反射获取到的参数个数是 0 !

结论

Java 中要获取到参数名,需要使用编译选项 -parameters ,然后就可以用反射类 Method 和 Parameter 来读取参数名了,但大多数情况下这样做并不理想,因为需要设置额外的编译参数,且对于 Grails 的 action 方法来说就是彻底不行,因为 Grails 将原来的 action 方法做了一个封装,因此用直接获取参数名的方法是不行的。

Groovy 中要获取参数名,其实也和 Java 中使用一样的技术,但因为 Grails 利用了 Groovy 的 Transformer 技术,动态给编译后的 class 文件添加了代码,所以我们也需要利用 Transformer 技术来实现获取参数名的功能。