问题现象

使用 MyBatis 执行包含多个参数的 Mapper 方法时,可能遇到“找不到参数”的异常:

MyBatis 参数名异常

异常中可选参数只有 arg0arg1param1param2,却没有源代码中的 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

两种 javac 编译方式

通过 javap 查看详细字节码:

javap -verbose ParameterNameDemo.class

启用选项后,可以看到 MethodParameters 中保存了 name

MethodParameters 字节码属性

也可以通过反射验证:

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 保存的是不同类型的字节码元数据。

参考:Oracle javac -parameters 说明