Spark 3.3.0在Java17运行单元测试报IllegalAccessError的原因及修复
根本原因
Spark 3.3.0 确实已通过SPARK-33772工单完成Java 17运行支持,但该支持并非零配置开箱即用:
- Spark官方自带的启动脚本(
spark-submit、spark-shell等)默认内置了Java 17所需的JVM模块开放参数,因此官方部署场景下不会触发该报错 - 通过maven-surefire-plugin运行单元测试时,插件会fork独立的测试JVM进程,该进程不会继承Spark脚本里的默认参数,直接使用Java 17默认的强模块封装策略
- Spark核心模块
StorageUtils需要访问java.base模块下的内部APIsun.nio.ch.DirectBuffer,在未主动开放模块权限的情况下,JVM会直接抛出IllegalAccessError,触发类初始化失败,后续所有依赖StorageUtils的Spark逻辑都会连带报错。
可行修复方案
按推荐优先级排序:
- 方案1:给maven-surefire-plugin配置Java 17要求的JVM参数,这是最稳定的长期修复方案
在项目pom.xml中修改surefire插件配置,在argLine中加入Spark官方要求的全量模块开放参数,配置示例如下:
其中<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M7</version> <configuration> <argLine> --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/sun.nio.cs=ALL-UNNAMED --add-opens=java.base/sun.security.action=ALL-UNNAMED --add-opens=java.base/sun.util.calendar=ALL-UNNAMED --add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED </argLine> </configuration> </plugin>--add-opens=java.base/sun.nio.ch=ALL-UNNAMED是解决当前报错的针对性参数,其余参数为Spark官方文档标注的Java 17运行必填参数,配置后可避免其他模块触发同类访问报错。 - 方案2:如果是直接在IDE中运行单元测试,在对应测试运行配置的「VM options」栏中填入上述
--add-opens参数即可 - 方案3:临时将测试环境JDK版本降级到Java 8或Java 11,这两个版本为Spark 3.3.0的长期兼容版本,无需额外配置模块参数即可正常运行单元测试,仅适合快速验证场景,不推荐作为长期方案
注意:截至Spark 3.5.x版本,Spark依然未移除对
sun.*系列内部API的依赖,所有自定义启动的Java 17进程(包括测试进程、Spring Boot内嵌Spark进程等)都需要配置上述模块开放参数,无法通过升级Spark小版本规避该问题。
内容的提问来源于stack exchange,提问作者Greg Kopff
相关产品推荐
相关产品推荐

