问题现象
使用 MyBatis 执行包含多个参数的 Mapper 方法时,可能遇到“找不到参数”的异常:
异常中可选参数只有 arg0、arg1 或 param1、param2,却没有源代码中的 tradeNo。最直接且稳定的解决方式,是显式使用 @Param:
PayOrder findOrder(
@Param("tradeNo") String tradeNo,
@Param("merchantId") String merchantId
);
另一条思路是在编译时保留真实参数名,这就是 javac -parameters 的作用。
-parameters 做了什么
默认编译时,方法参数的真实名称不一定写入 .class 文件。启用 -parameters 后,编译器会把构造器和方法的形式参数名写入 MethodParameters 属性,反射 API 便可以读取它们。
示例类:
class ParameterNameDemo {
void test(String name) {
System.out.println(name);
}
}
分别编译:
javac ParameterNameDemo.java
javac -parameters ParameterNameDemo.java
通过 javap 查看详细字节码:
javap -verbose ParameterNameDemo.class
启用选项后,可以看到 MethodParameters 中保存了 name:
也可以通过反射验证:
Method method = ParameterNameDemo.class
.getDeclaredMethod("test", String.class);
for (Parameter parameter : method.getParameters()) {
System.out.println(parameter.getName());
System.out.println(parameter.isNamePresent());
}
在构建工具中启用
Maven
使用 maven-compiler-plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<parameters>true</parameters>
</configuration>
</plugin>
也可以使用原始编译参数:
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
Gradle
tasks.withType(JavaCompile).configureEach {
options.compilerArgs += ['-parameters']
}
与 MyBatis 的关系
MyBatis 可以在运行时读取参数名,但能否读取到真实名称取决于:
- 编译产物是否包含
MethodParameters。 - MyBatis 的
useActualParamName配置及版本行为。 - 方法是否被代理、混淆或经过额外字节码处理。
因此,即使全局启用了 -parameters,对关键 Mapper 方法继续使用 @Param 仍然更明确,也能减少构建配置差异造成的问题。
不要把调试信息中的局部变量表与 MethodParameters 混为一谈:-g 和 -parameters 保存的是不同类型的字节码元数据。