--add-exports是Java 9模块系统中用于运行时临时开放封装包的JVM参数,解决IllegalAccessError等因模块封装导致的内部API访问失败问题,需在启动时指定源模块/包与目标模块,不可动态添加。
java 9 引入模块系统(jpms)后,--add-exports 是一个 jvm 启动参数,用于在运行时**临时开放**某个模块中被封装(package-private 或 module-private)的包,使其对指定目标模块(或所有模块)可见。它不修改源码、不重新编译,仅影响当前 jvm 实例的类加载行为。
什么时候需要 --add-exports
当你遇到类似以下错误时,说明代码试图访问一个被模块系统默认封禁的内部 API:
java.lang.IllegalAccessError: class xxx cannot access class yyy (xxx is in unnamed module of loader ...; yyy is in module java.base)- 使用反射调用
sun.misc.Unsafe、jdk.internal.ref.Cleaner等 JDK 内部类 - 第三方库(如某些旧版 Hibernate、Netty、JUnit 5 早期版本)依赖了 JDK 的非公开 API
基本语法与常见写法
--add-exports 格式为:--add-exports
-
开放给所有模块:用
ALL-UNNAMED(适用于类路径加载的代码)或ALL-SYSTEM(更激进,慎用)
示例:--add-exports java.base/jdk.internal.ref=ALL-UNNAMED -
开放给特定模块:比如你的模块叫
my.app,要让它访问java.desktop中的com.sun.java.swing.plaf.windows
示例:--add-exports java.desktop/com.sun.java.swing.plaf.windows=my.app -
多个导出需重复使用该选项**:
--add-exports A/p1=B --add-exports A/p2=C
实际使用注意事项
这个选项必须作为 JVM 参数传入,不能在代码中动态添加;且只对当前启动生效。
- 放在
java命令中-cp或--module-path之后、主类名之前,例如:java --add-exports java.base/jdk.internal.ref=ALL-UNNAMED -cp myapp.jar MyApp - 若用 Maven,可在
maven-surefire-plugin或maven-failsafe-plugin的中配置 - IDE(如 IntelliJ)需在
Run Configuration → VM Options中手动添加 - 注意:JDK 16+ 默认禁止反射访问强封装类(即使用了
--add-exports),还需配合--illegal-access=permit(JDK 16~17)或--add-opens(推荐替代反射场景)
比 --add-exports 更安全的替代方案
除非明确需要导出(即让某包的 public 类/成员对其他模块“可见”),否则优先考虑:
-
--add-opens:用于反射场景(如setAccessible(true)),它打开包的深层反射权限,比--add-exports更精准、更受支持 - 升级依赖库:很多老库已适配 JPMS,避免直接依赖内部 API
- 使用标准替代 API:例如用
java.lang.invoke.VarHandle替代Unsafe,用Cleaner的公共构造方式而非反射获取










