Java多版本兼容测试:Java 17中Mockito.spy Graphics2D的非法访问问题及跨版本解决方案咨询
解决Java多版本兼容下Mockito Spy Graphics2D的测试问题
针对你遇到的Java 8/11/17多环境测试中Mockito Spy SunGraphics2D的冲突问题,这里有几个实用的解决方案,不需要在CI环境中手动切换命令参数:
方案一:通过Maven Surefire插件条件化配置JVM参数
这是最直接的解决方式,利用Maven的Profile或内置条件判断,让Surefire插件仅在Java 17及以上版本添加--add-opens参数。
方式1:使用Maven三元表达式(Maven 3.2.1+支持)
在你的pom.xml中配置Surefire插件,通过java.version属性判断版本:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.1.2</version> <!-- 建议使用最新稳定版,确保兼容性 --> <configuration> <argLine> ${java.version>=17? '--add-opens=java.desktop/sun.java2d=ALL-UNNAMED' : ''} </argLine> </configuration> </plugin> </plugins> </build>
这样当Java版本≥17时,自动添加所需参数;Java 8/11时参数为空,不会触发JVM启动错误。
方式2:使用Maven Profile自动激活
如果你的Maven版本较低不支持三元表达式,可以用Profile来实现:
<profiles> <profile> <id>java-17-plus</id> <activation> <jdk>[17,)</jdk> <!-- Java 17及以上自动激活 --> </activation> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <argLine>--add-opens=java.desktop/sun.java2d=ALL-UNNAMED</argLine> </configuration> </plugin> </plugins> </build> </profile> </profiles>
Profile会根据当前JDK版本自动激活,完全不需要手动干预。
方案二:自定义Graphics2D委托类,避开内部类依赖
如果不想依赖JVM参数配置,可以通过包装类绕过对SunGraphics2D的直接Spy。我们可以写一个简单的委托类,把真实的Graphics2D实例包装进去,然后Spy这个自定义类:
1. 实现委托类
public class DelegatingGraphics2D extends Graphics2D { private final Graphics2D delegate; public DelegatingGraphics2D(Graphics2D delegate) { this.delegate = delegate; } // 重写业务代码中需要用到的方法,全部委托给原始实例 @Override public Color getColor() { return delegate.getColor(); } // 根据你的测试需求,添加其他需要的方法,比如drawString、fillRect等 @Override public void drawString(String str, int x, int y) { delegate.drawString(str, x, y); } // 注:如果需要重写的方法太多,可以考虑用动态代理,但测试场景下只需要覆盖用到的方法即可 }
2. 修改测试代码
@Test public void testGraphics() throws Exception { BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB); Graphics2D originalGraphics = bi.createGraphics(); // Spy自定义的委托类,而非原始的SunGraphics2D DelegatingGraphics2D graphics = Mockito.spy(new DelegatingGraphics2D(originalGraphics)); // 业务测试逻辑不变 graphics.getColor(); Mockito.verify(graphics, times(1)).getColor(); }
这个方案彻底避开了对Sun内部类的反射访问,所有Java版本都能正常运行,不需要任何JVM参数。
方案三:升级Mockito及依赖(辅助优化)
虽然你当前用的Mockito 4.6.1已经支持Java 17,但确保依赖的ByteBuddy版本是最新的(Mockito 4.x对应ByteBuddy 1.12+),可以减少模块系统下的反射问题。不过这个方案通常需要配合方案一的参数配置才能彻底解决问题。
总结
- 如果不想修改测试代码,优先选方案一,通过Maven配置自动适配不同Java版本;
- 如果想彻底摆脱对内部API的依赖,推荐方案二,代码层面解决兼容性问题,更稳定。
内容的提问来源于stack exchange,提问作者gawi




