illegalaccesserror在java 9+中多为运行时模块访问控制失败所致,非编译问题;需检查exports/opens声明、jvm参数--add-opens及依赖模块是否适配jpms。

IllegalAccessError 在 Java 9+ 模块化环境中,常被误认为是编译期问题,实则多为运行时模块访问控制失败所致。它和 IllegalAccessException 不同:后者是反射中显式调用受检异常,而前者是 JVM 在解析符号引用时发现违反模块可访问性规则(如跨包访问非导出类、使用非开放的包进行反射),直接抛出的致命错误。
确认是否由模块系统触发
不是所有 IllegalAccessError 都源于模块系统,但若满足以下任一条件,高度可疑:
- 错误堆栈中出现
java.lang.Module、jdk.internal.module.Modules或提示module not exported/not open to module - 项目已声明
module-info.java,且涉及跨 JAR 包的私有类/包访问(尤其常见于旧库未适配模块化) - 使用了反射(如
Class.getDeclaredField()、Method.setAccessible(true))访问非开放包中的成员
检查模块声明与导出(exports / opens)
核心原则:**一个模块要让其他模块访问其包内的 public 类型,必须显式 exports;若需反射访问(如序列化、框架注入),还需 opens。**
- 查看报错类所在模块的
module-info.java,确认目标包是否在exports列表中。例如:exports com.example.internal.utils;—— 错误!internal包本就不该导出 - 若框架(如 Spring、Jackson)通过反射访问你的类字段,你必须
opens对应包:opens com.example.model to spring.core, com.fasterxml.jackson.databind; - 对 JDK 内部 API(如
sun.misc.Unsafe)的访问,模块系统默认禁止。此时需启动参数:--add-opens java.base/java.lang=ALL-UNNAMED(慎用,仅临时调试)
诊断依赖链中的“隐式强封装”
第三方库若未声明模块(即 Automatic-Module-Name 缺失或为 unnamed module),JVM 默认对其启用强封装(strong encapsulation)。此时即使你模块导出了包,自动模块也无法访问它。
- 运行时加
--illegal-access=debug(Java 9–15)或--add-opens=ALL-UNNAMED(Java 16+)观察警告,定位哪个模块尝试非法访问 - 用
jdeps --list-deps --module-path mods/ your-app.jar查看模块依赖图,识别未命名模块(unnamed module)与你的模块间是否有非法跨包调用 - 升级依赖:优先选用已声明模块信息(含
module-info.class)的库版本,或向维护者提交 issue
避免反射滥用,改用模块友好的替代方案
很多 IllegalAccessError 源于框架或自定义代码强行绕过封装。应主动重构:
- 将需反射访问的类移到显式
opens的包中,而非放在internal或默认包下 - 用服务加载机制(
ServiceLoader)替代直接 new 实例或反射调用私有构造器 - 框架配置优先:如 Jackson 使用
@JsonCreator+ 公共静态工厂方法,而非依赖setAccessible(true)访问 private 字段
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











