SpringMVC 为什么可以解析方法参数名称,MyBatis 不行?¶
结论¶
SpringMVC 能用 -parameters 编译标志(或 ASM 读取 LocalVariableTable)拿到方法参数名;MyBatis 默认拿不到,必须加 @Param 或开启编译参数保留。 根本原因是:Java 编译后默认不保留局部变量名,拿不拿得到取决于框架是否主动去 class 字节码里读。
一、Java 字节码里的参数名¶
Java 编译后的 class 文件,方法参数名默认被擦除(变成 arg0、arg1)。要保留真实参数名,有两种方式:
方式 1:编译时加 -parameters¶
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<parameters>true</parameters>
</configuration>
</plugin>
这样参数名写入 MethodParameters 属性(JDK 8+)。
方式 2:保留调试信息(LocalVariableTable)¶
编译加 -g,class 文件会带 LocalVariableTable,ASM 可以解析出参数名。IDEA 默认开启。
二、SpringMVC 怎么拿到参数名¶
SpringMVC 用 DefaultParameterNameDiscoverer,它聚合了多个发现器:
StandardReflectionParameterNameDiscoverer:用-parameters编译后的MethodParameters。LocalVariableTableParameterNameDiscoverer:用 ASM 读 LocalVariableTable。
所以即使你不加 -parameters,只要 IDE 编译时带调试信息,SpringMVC 也能拿到。
@GetMapping("/user")
public User get(@RequestParam String name) { ... }
// 不加 value 也能识别 name,因为参数名被读取
三、MyBatis 为什么默认拿不到¶
MyBatis 3.4.1 之前不读取 LocalVariableTable,只看 @Param 注解。3.4.1 之后引入了 ParamNameResolver,但默认行为是:
- 如果方法参数没加
@Param,且没开-parameters,MyBatis 会用arg0/arg1或param1/param2。 - 在 IDE(带
-g)运行时,有时能拿到名字;打 jar 用 Maven 默认编译就拿不到。
// Mapper 接口
User selectBy(String name, Integer age);
<!-- XML 中 -->
<select id="selectBy" resultType="User">
SELECT * FROM user WHERE name = #{name} AND age = #{age}
</select>
<!-- 报错:找不到 name 属性,要用 #{arg0} / #{param1} -->
四、解决方法¶
- 加
@Param(最稳): - 编译加
-parameters(全局生效)。 - MyBatis 配置:
本质
不是 SpringMVC "魔法",而是它更早、更完整地封装了"读参数名"的逻辑;MyBatis 把这个责任推给了 @Param。理解到"字节码默认不保留参数名"这一层,答案就完整了。