opens 是 java 9 模块化中专为反射访问设计的关键字,仅在运行时允许指定模块反射读写私有/受保护/包私有成员,不改变编译可见性;当 spring、jackson、junit 等需反射访问 private 字段、protected 方法、声明注解或反序列化时,必须用 opens 而非 exports。

Java 9 模块化系统中,opens 是专为反射访问设计的关键字,它不改变编译期可见性,也不导出 API,只在运行时允许指定模块通过反射读写目标包内的私有、受保护或包私有成员。
什么时候必须用 opens
当你的代码或依赖框架(如 Spring AOP、Jackson、JUnit、Hibernate)需要通过反射访问以下内容时,仅 exports 不够,必须用 opens:
- 读取或修改 private 字段(例如 Lombok 生成的 @Data 类字段)
- 调用非 public 方法(比如 protected 初始化逻辑)
- 获取类上声明的注解(
getDeclaredAnnotations()) - 反序列化含私有字段的对象(
ObjectInputStream) - 测试框架访问内部状态(Mockito、JUnit ParameterResolver)
在 module-info.java 中正确声明
若你控制该模块,且目标包(如 com.example.service)需被其他模块反射访问,就在 module-info.java 中写:
opens com.example.service to java.base;
这表示:允许 java.base 模块(包含反射 API)在运行时反射访问该包内所有类及其成员。
常见组合写法:
- 开放给多个模块:
opens com.example.config to spring.beans, spring.core; - 开放给所有未命名模块(传统 classpath 启动方式):
opens com.example.model;(等价于to ALL-UNNAMED) - 只开放给测试模块:
opens com.example.domain to com.example.app.test;
注意:opens 必须写在 exports 之后,不能拼错为 open,也不能作用于子包(需显式声明)。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
对 JDK 内部包只能用 JVM 参数
你无法修改 java.base 等系统模块的 module-info.class,所以必须通过启动参数强制开放:
--add-opens java.base/java.lang=ALL-UNNAMED
典型常用参数:
-
--add-opens java.base/java.util=ALL-UNNAMED(应对 ArrayList、HashMap 等私有字段) -
--add-opens java.base/java.io=ALL-UNNAMED(解决 File.path、FileDescriptor 访问) -
--add-opens java.base/sun.security.action=ALL-UNNAMED(部分安全相关反射)
这些参数必须放在 java 命令最前面,且在 -jar 或 -cp 之前,否则无效;大小写与斜杠必须完全匹配。
验证是否生效
运行时可检查异常类型和日志:
- 抛出
InaccessibleObjectException→ 明确是模块访问限制问题 - 加启动参数
--illegal-access=debug,JVM 会打印拒绝细节 - 在代码中调用
Module.getDescriptor().opens()查看当前模块已开放哪些包
setAccessible(true) 在模块化环境下无法绕过 opens 限制,它只作用于访问控制检查,不作用于模块封装层。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










