JDK 9+ 模块化下 Spring 反射访问私有字段需用 --add-opens 主动开放权限,如 --add-opens java.base/java.lang=ALL-UNNAMED,优先配置 java.base 下 lang/util/time/net 四个包,Maven/Gradle 可固化参数,避免非法语法和混淆 exports/opens。

Spring 等框架依赖反射访问私有字段(如 @Autowired 注入私有成员、@Value 设置私有属性),但在 JDK 9+ 模块化系统下,这种访问默认被禁止,会抛出 InaccessibleObjectException。解决的关键不是绕过模块限制,而是用 --add-opens 主动、精准地开放所需包的反射权限。
明确哪些包需要 opens
常见报错指向三个核心模块:
-
java.base:常需开放java.lang、java.util、java.time、java.net等子包,因 Spring 反射操作ClassLoader、String、ArrayList等内部结构 -
java.desktop:若用到 AWT/Swing 相关类(较少见,但部分老工具类可能触发) -
jdk.unsupported:仅当代码显式调用sun.misc.Unsafe等非标准 API 时才需开放(不推荐,应优先替换为标准替代方案)
在启动参数中配置 --add-opens
这是最直接有效的兼容方式。以 Spring Boot 应用为例,在运行命令中添加:
--add-opens java.base/java.lang=ALL-UNNAMED \ --add-opens java.base/java.util=ALL-UNNAMED \ --add-opens java.base/java.time=ALL-UNNAMED \ --add-opens java.base/java.net=ALL-UNNAMED
说明:
-
java.base/java.lang表示对java.base模块中的java.lang包开放反射 -
ALL-UNNAMED表示向所有未命名模块(即传统 classpath 下的应用代码和第三方 jar)开放;若使用模块化应用,可指定具体模块名 - 多个
--add-opens可并列使用,顺序无关
在构建工具中固化配置(避免手动传参)
Maven(pom.xml):
<plugin><groupid>org.springframework.boot</groupid><artifactid>spring-boot-maven-plugin</artifactid><configuration><jvmarguments>
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
</jvmarguments></configuration></plugin>
Gradle(build.gradle):
bootRun {
jvmArgs = [
'--add-opens', 'java.base/java.lang=ALL-UNNAMED',
'--add-opens', 'java.base/java.util=ALL-UNNAMED'
]
}
这样每次执行 mvn spring-boot:run 或 ./gradlew bootRun 都自动携带参数,开发阶段更省心。
验证是否生效与常见误区
启动后观察日志,若不再出现 InaccessibleObjectException,且 Bean 注入、配置绑定等功能正常,说明配置已起效。
注意避坑:
- 不要盲目加
--add-opens java.base/all-opens=ALL-UNNAMED—— 该语法非法,all-opens不是合法模块名 - 不要混淆
--add-exports和--add-opens:exports解决编译期可见性,opens才解决运行时反射访问 - Spring Boot 3 + JDK 17 是主流组合,上述四个
--add-opens已覆盖 95% 的注入场景;个别自定义类或第三方库触发新包时,按异常提示追加即可








