查看原文
其他

MyBatis凭神马能获取到接口参数名?

以下文章来源于程序员自学之道 ,作者绪扬IS未知数

本文转载公众号:程序员自学之道

上一篇文章 《面试官:为什么SpringMVC可以正确解析方法参数名称,但MyBatis却不行?》中,咱们一起讨论了通过 -g 参数让字节码文件包含局部变量表,然后使用 ASM 解析字节码文件读取局部变量表,来获取方法参数名。

最后留下了一个问题:虽然通过字节码的方法的确可以拿到参数名,但使用不方便,而且它对接口和抽象方法的参数名也无能为力。有没有更简单的方式,不需要解析字节码又能支持抽象方法呢

答案是:有的。就是 -parameters 参数,它是 Java8 的新特性。

-parameters 参数

Java8 在反射包中加入了 java.lang.reflect.Parameter 类来获取参数相关的信息

A small but useful example is support for method parameter names at run time: storing such names in the class file structure goes hand in hand with offering a standard API to retrieve them (java.lang.reflect.Parameter) - 《The Java Virtual Machine Specification》

但是它依赖于编译时添加 -parameters 参数,也就是说,只有在编译的时候添加了这个参数才能在运行时通过反射获取参数信息。还是用我们的 HelloWorld 程序,我们来试一下添加 -parameters 参数:

  1. javac -parameters HelloWorld.java

  2. javap -verbose HelloWorld.class

可以看到,字节码文件中多了 MethodParameters 部分,里面存放的就直接是我们所需要在的参数名!就像《Java 虚拟机规范》里说明的,我们可以直接通过反射获取:

  1. HelloWorld.class.getMethod("main",String[].class).getParameters()[0].getName()

问题来了,我们如何在编译的时候自动加上 -parameters 这个参数呢?毕竟我们不可能只在自己的 IDE 上做设置,也不可能自己写脚本来编译。

如果你使用 maven 来管理项目的话,可以直接通过插件来完成:

  1. <build>

  2. <plugins>

  3. <plugin>

  4. <groupId>org.apache.maven.plugins</groupId>

  5. <artifactId>maven-compiler-plugin</artifactId>

  6. <version>3.8.0</version>

  7. <configuration>

  8. <source>${java.version}</source>

  9. <target>${java.version}</target>

  10. <parameters>true</parameters>

  11. </configuration>

  12. </plugin>

  13. </plugins>

  14. </build>

这样这个 -parameters 参数就会在编译的时候自动加上了

关于 SpringBoot

之前曾提到,SpringBoot 已经自动解决了参数名解析的问题,它其实就是通过 -parameters 参数来实现的。在 spring-boot-starter-parent.pom 文件中它为我们添加了上面提到的插件及参数: 有了这个参数而且是在 JDK8+ 中运行的话无论是 SpringMVC 还是 MyBatis 都可以获取到正确的方法参数名了!

总结

获取参数名称的方式

  1. 编译时添加 -g 参数,然后通过解析字节码读取局部变量表获取

    maven 在编译时会自动添加这个参数,但是解析字节码很不方便,而且因为接口和抽象方法没有方法体,也就没有局部变量,自然也就没有局部变量表,所以无法通过局部变量表来获取参数名称。

  2. Java8 编译时添加 -parameters 参数,然后通过反射获取

    可以通过配置 maven 插件自动添加,使用非常方便,直接通过反射即可拿到参数信息。但是需要 Java8 及以上才能使用。

SpringMVC 和 MyBatis 

  • 有 -parameters 参数的场景两个框架都可以正确解析参数名。

  • 只有 -g 参数时:

    • SpringMVC 通过解析字节码获取 Controller 的方法参数以绑定请求参数

    • MyBatis 需要与接口绑定,而 -g 参数对接口和抽象类无效,因此不能正确解析参数名

  • -g 和 -parameters 都没有时,两者都无法正确解析参数名

后记

不知不觉写了这么多,现在也快凌晨两点了。

对于获取方法参数名这个问题的探究最早其实是来源于我在写 http-api-invoker 框架(github 地址:https://github.com/dadiyang/http-api-invoker) 的时候意识到的。这个框架跟MyBatis类似,它将接口与 url 进行绑定然后生成代理类来发送 http 请求,我们无需关注参数拼接和序列化、请求发送和返回值处理的过程,只需要定义好我们的接口并打上注解即可。

在不断优化和使用的过程中我发现,每个接口方法都需要打 @Param 注解太麻烦,而 MyBatis 也同样有这个问题,然而我发现 SpringMVC 却可以解决。因此为了更加完善这个框架,我开始一探究竟。期间做了很多的功课,把整个来龙去脉都了解清楚了,但是一直没有时间整理。

现在终于忙里偷闲趁着周末把这篇文章写出来了,可惜由于最近我让 http-api-invoker 框架兼容到了 JDK6,还没有想好怎样让它在支持 JDK6 的前提下更好地利用 JDK8+ 的 -parameters 特性。这个留到以后再做进一步的探索吧。

热文推荐

一个正则表达式怎么会引起线上CPU狂飙?

原创:如何来判断一个List是否有序?


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存