从Java 8迁移到Java 17后测试报InaccessibleObjectException,是否因版本变更?
问题分析与解决方案
核心原因
这确实是Java 17(本质是Java 9引入的模块化系统JPMS)的机制变更导致的问题,并非单纯的版本不匹配。
Java 9开始引入模块系统后,JDK核心模块(如java.base)默认会封装内部API和受保护方法,限制外部通过反射访问。java.lang.Object.clone()是java.base模块中java.lang包下的protected native方法,而你的测试代码属于未命名模块(大多数非模块化项目默认如此),java.base模块没有将java.lang包开放给未命名模块,因此当Whitebox.invokeMethod尝试通过反射强行访问该方法时,就会抛出InaccessibleObjectException。Java 17进一步加强了模块化封装限制,这类反射访问的约束比Java 8严格得多。
解决方案
1. 添加JVM参数开放模块访问
最直接的解决方式是在测试运行时添加JVM参数,让java.base模块将java.lang包开放给所有未命名模块。你需要在Maven的maven-surefire-plugin中配置argLine参数:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <!-- 建议升级到适配Java 17的版本,比如3.0.0及以上 --> <version>3.2.2</version> <configuration> <argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine> </configuration> </plugin> </plugins> </build>
注意:maven-surefire-plugin旧版本对Java 17支持不佳,建议升级到3.0.0以上的稳定版本。
2. 替换Whitebox的反射用法
如果业务允许,尽量避免通过反射访问受保护的JDK核心方法:
- 如果测试目标类自身重写了
clone()方法并公开了访问权限,直接调用该方法即可; - 替换为更符合模块化规范的测试工具,比如使用JUnit 5内置的反射工具类,或Mockito的反射API替代
Whitebox。
3. 升级测试依赖版本
你的junit-jupiter-engine版本5.8.2发布于2021年,对Java 17的兼容性有限。建议升级到5.9.x及以上版本,能更好地适配Java 17的模块化特性,减少潜在兼容性问题:
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.10.1</version> <scope>test</scope> </dependency>
内容的提问来源于stack exchange,提问作者RagaSGNur
相关产品推荐
相关产品推荐

