
本文详解如何在 JDK 17+ 环境中通过正确配置 Maven Surefire 和 Compiler 插件,解决因模块封装导致的 InaccessibleObjectException 测试失败问题,确保 --add-opens JVM 参数被可靠传递至测试执行进程。
本文详解如何在 jdk 17+ 环境中通过正确配置 maven surefire 和 compiler 插件,解决因模块封装导致的 `inaccessibleobjectexception` 测试失败问题,确保 `--add-opens` jvm 参数被可靠传递至测试执行进程。
从 JDK 9 引入模块系统(JEP 261)起,Java 对反射访问施加了严格限制;升级到 JDK 17 后,java.base 等核心模块默认不再向未命名模块(如传统 classpath 应用)开放内部包。因此,当测试代码(尤其是使用 Mockito、JUnit 5 反射机制或某些 ORM/序列化库)尝试访问 java.lang.Object.clone() 或 java.util.concurrent 中受保护成员时,便会抛出:
java.lang.reflect.InaccessibleObjectException: Unable to make protected native java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException accessible: module java.base does not "opens java.lang" to unnamed module
虽然在 IntelliJ 中可通过测试运行配置手动添加 --add-opens 参数临时绕过,但该设置仅作用于 IDE 运行环境,对 Maven 构建完全无效。关键在于:Maven Surefire 插件必须显式将这些 JVM 参数注入测试 fork 进程,且需与编译阶段协同——否则编译器可能因缺少模块权限而无法生成兼容字节码。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
✅ 正确配置 Surefire 插件(核心修复)
@#@#@#@#@#@#@#@#@#@0、@#@#@#@#@#@#@#@#@#@1,它们已内置模块适配逻辑。<li>
<strong>避免过度开放</strong>:生产环境慎用 <code>ALL-UNNAMED,可精确指定目标模块名(如 --add-opens java.base/java.lang=org.mockito),提升安全性。
完成上述配置后,执行 mvn clean test 即可成功运行测试。此方案兼顾构建可重现性与跨环境一致性,是 JDK 17+ 迁移中处理模块反射限制的标准实践。
<plugin><groupid>org.apache.maven.plugins</groupid><artifactid>maven-surefire-plugin</artifactid><version>3.1.2</version><configuration><skiptests>${skip.surefire.integration.tests}</skiptests><forkcount>10</forkcount><reuseforks>false</reuseforks><argline>
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/java.util.concurrent=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
<!-- 如需其他包(如 java.time、java.net),按相同格式追加 -->
--add-opens java.base/java.time=ALL-UNNAMED
</argline></configuration></plugin>
<plugin><groupid>org.apache.maven.plugins</groupid><artifactid>maven-compiler-plugin</artifactid><version>3.11.0</version><configuration><source>17</source><target>17</target><compilerargs><arg>--add-opens</arg><arg>java.base/java.lang=ALL-UNNAMED</arg><arg>--add-opens</arg><arg>java.base/java.lang.reflect=ALL-UNNAMED</arg><arg>--add-opens</arg><arg>java.base/java.util.concurrent=ALL-UNNAMED</arg><arg>--add-opens</arg><arg>java.base/java.util=ALL-UNNAMED</arg></compilerargs></configuration></plugin>
module my.app {
requires java.base;
opens java.lang to org.junit.jupiter.api;
opens java.util to org.mockito;
}










