java 9+模块化后反射读取注解失败,根本原因是模块未显式opens包给反射调用方(如java.base),仅exports不够;必须在module-info.java中声明opens com.example.anno to java.base等对应模块,setaccessible(true)无法绕过该限制。

Java 9 引入模块系统(JPMS)后,反射读取注解可能失败——不是因为代码写错了,而是模块未显式开放(opens)或导出(exports)相关包给反射所需的模块(如 java.base 中的 java.lang.reflect)。默认情况下,模块是封装的,即使注解本身用 @Retention(RUNTIME) 声明,若目标类所在的包未对反射调用方“开放”,getDeclaredAnnotations() 等方法会抛出 InaccessibleObjectException。
模块必须显式 opens 包给反射调用方
仅 exports 不够——exports 控制编译期类型可见性,而反射访问私有/受保护成员(包括注解元数据)需要 opens(运行时反射访问权限)。
- 若注解定义在模块
my.annotation.module的com.example.anno包中,且你想从主模块或测试模块中通过反射读取它,需在该模块的module-info.java中添加:
注意:to java.base 是关键——因为反射 API(Method.getAnnotation() 等)属于 java.base 模块。若调用方是自定义模块(如 my.reflection.tool),则应写为:
使用 setAccessible(true) 无法绕过模块封装
在非模块化环境里,对 AccessibleObject(如 Method)调用 setAccessible(true) 可突破访问控制;但在模块化环境下,这不再生效——模块层的封装优先级高于反射的暴力访问。即使你写了 method.setAccessible(true),只要模块未 opens 对应包,仍会失败。
- 错误示范:仅靠反射调用无法解决模块限制
- 正确做法:模块声明中补全
opens,再配合反射代码
验证模块是否已正确开放
运行时可通过以下方式检查包是否对指定模块开放:
- 启动 JVM 时加参数
--illegal-access=debug,会打印详细拒绝原因 - 捕获异常并检查类型:
if (e instanceof InaccessibleObjectException),说明是模块访问问题 - 用
Module.getDescriptor().opens()查看当前模块开放了哪些包及开放对象
常见组合场景与配置建议
多数实际项目涉及三方框架(如 JUnit、Spring)或测试模块读取业务模块注解:
-
JUnit 5 测试读取被测模块注解:被测模块需
opens com.example.service to org.junit.jupiter.api; -
Spring Boot 应用扫描自定义注解:启动模块(如
myapp)应opens所有含注解的包给spring.core或java.base(取决于 Spring 版本) -
工具类模块通用读取:若开发一个通用注解处理器模块
anno-processor,建议其module-info.java声明requires java.base;,并要求使用者模块opens目标包给它
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











