背景

今天测试环境出现了一个错误,错误原因很简单,Mybatis Mapper文件在传递参数的时候没有使用 Param注解。 在Mybatis中,如果要传递多个参数必须使用 @Param 注解,否则会出现获取参数失败的情况。

作为一个有点懒惰的开发,我是希望把 @Param 注解去掉的,于是我去Github Mybatis的issue搜索了一圈,发现也有同学有这样的问题和需求。

于是我就开始想,为什么SpringMVC可以获取到参数,而Mybatis就不行呢,于是我打算用ASM获取一下参数(SpringMVC是使用ASM来做的),我立马就把以前的代码Copy过来

  • 调试一下带body的方法,跑通了。
  • 又测试了一个Interface,失败了。

    调试

    于是在休息的时候,我开始对ASM的过程进行debug,使用过ASM的同学都知道,他是对class文件进行依次visit处理的过程,结果却发现interface的method压根就进不去哪些方法,直接来到了visitEnd. 自然就获取不到参数了。

我马上开始Google, 中文网上都是对ASM的介绍,于是我又去stackoverflow查看问题,果然被我查到了。但是这里的结论只是描述了interface的method是不行的,但是为什么不行,则没有进行描述。这个有点不符合stackoverflow的提问哲学。

emmmmm,我最后把我的发现结果回复了这个问题,还迎来了2个反对,丢失了4分,得不偿失,得不偿失呀 🤣🤣🤣🤣🤣🤣🤣

https://stackoverflow.com/questions/2866805/can-asm-method-visitors-be-used-with-interfaces/72989593#72989593

果然我遇到的问题,都有人遇到过了。

我又想到ASM issue去看看什么情况,但是也没有找到问题,在找的过程中,猛滴想到ASM本身就是解析class文件的, 于是使用javap查看情况。

  1. public interface FF {
  2. default void hh(String name) {
  3. }
  4. String hello(String hello);
  5. }

Javap执行结果,如下所示,interface的hello方法,是没有body体的,同时也没有LocalVariableTable,而我如果要获取参数,就必须以来这里的LocalVariableTable。所以我无法获取到参数,只能通过注解来实现。

  1. ~ javap -v /Users/chenshun/tool/winter/target/test-classes/io/github/chenshun00/web/asm/FF.class
  2. Classfile /Users/chenshun/tool/winter/target/test-classes/io/github/chenshun00/web/asm/FF.class
  3. Last modified 2022-7-15; size 384 bytes
  4. MD5 checksum de24290be11cb7706f83d3a355261367
  5. Compiled from "FF.java"
  6. public interface io.github.chenshun00.web.asm.FF
  7. minor version: 0
  8. major version: 52
  9. flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
  10. Constant pool:
  11. #1 = Class #16 // io/github/chenshun00/web/asm/FF
  12. #2 = Class #17 // java/lang/Object
  13. #3 = Utf8 hh
  14. #4 = Utf8 (Ljava/lang/String;)V
  15. #5 = Utf8 Code
  16. #6 = Utf8 LineNumberTable
  17. #7 = Utf8 LocalVariableTable
  18. #8 = Utf8 this
  19. #9 = Utf8 Lio/github/chenshun00/web/asm/FF;
  20. #10 = Utf8 name
  21. #11 = Utf8 Ljava/lang/String;
  22. #12 = Utf8 hello
  23. #13 = Utf8 (Ljava/lang/String;)Ljava/lang/String;
  24. #14 = Utf8 SourceFile
  25. #15 = Utf8 FF.java
  26. #16 = Utf8 io/github/chenshun00/web/asm/FF
  27. #17 = Utf8 java/lang/Object
  28. {
  29. public void hh(java.lang.String);
  30. descriptor: (Ljava/lang/String;)V
  31. flags: ACC_PUBLIC
  32. Code:
  33. stack=0, locals=2, args_size=2
  34. 0: return
  35. LineNumberTable:
  36. line 17: 0
  37. LocalVariableTable:
  38. Start Length Slot Name Signature
  39. 0 1 0 this Lio/github/chenshun00/web/asm/FF;
  40. 0 1 1 name Ljava/lang/String;
  41. public abstract java.lang.String hello(java.lang.String);
  42. descriptor: (Ljava/lang/String;)Ljava/lang/String;
  43. flags: ACC_PUBLIC, ACC_ABSTRACT
  44. }
  45. SourceFile: "FF.java"

同时,我也发现了,这种问题不是个例,例如feign的interface定义,也必须是要使用注解的,可以是springmvc的,也可以使用feign本身的。其原因大概如此。

总结

在移除 Param 注解路上,发现了为什么SpringMVC为什么可以不需要注解,而Mybatis又必须依赖注解,解决问题的路上稍微有点曲折,但是最终还是解决了。 丢失了4分我有些不解

so bad,在我写这个的时候又收获了一个反对票,-6分。