要让框架内私有变量跨模块访问,关键用 exports to 精准授权编译期可见性,而反射访问私有成员必须用 opens to;例如 exports com.example.config to spring.core 仅允许 spring.core 编译期引用,且需配合 requires spring.core 和 public 类声明。

要让框架内私有变量(如 Spring、Hibernate、JUnit 等)跨模块访问,关键不是“绕过”封装,而是用 exports to 精准授权——它允许你只对特定模块开放包的编译期可见性,既满足框架运行需求,又避免全局暴露。
明确目标:哪些变量需要被谁访问?
先区分两类访问场景:
-
框架调用 public 类的 public 成员 → 只需
exports com.example.api; -
框架反射操作 private/package-private 字段或方法(如 Jackson 序列化、Spring Bean 注入、JUnit 测试私有逻辑)→ 必须用
opens to,exports to不管用
注意:exports to 控制的是“能不能在代码里直接 new、import、调用”,不解决反射问题。如果你误以为它能放开私有字段访问,会遇到 IllegalAccessException。
正确使用 exports to 的三步实操
假设你有一个内部配置类 com.example.config.InternalConfig,它含 public 方法但需被 Spring 框架模块加载并实例化:
-
第一步:在提供方模块声明导出限制
在module-info.java中写:exports com.example.config to spring.core;
→ 表示仅spring.core模块可编译期引用该包;其他模块连 import 都报错。 -
第二步:消费方模块声明依赖
Spring 模块(或你的应用模块)必须写:requires spring.core;
否则即使你 exports to 它,也无法解析类型。 -
第三步:确保类本身是 public
InternalConfig必须声明为public class InternalConfig;若只是包私有类(default 修饰),exports to完全无效——JVM 根本看不到这个类。
常见陷阱与避坑提示
以下写法看似合理,实则无效或危险:
-
exports com.example.internal to *;→ Java 不支持通配符,编译失败 -
exports com.example.internal to my.app;但my.app模块未在requires中声明 → 编译通过但运行时报NoClassDefFoundError - 想让 Jackson 访问
private String token;却只写exports to java.base→ 无用,必须改用opens com.example.model to com.fasterxml.jackson.databind; - 模块路径未启用(仍用
-cp启动)→module-info.java全部失效,退化为传统类路径行为
搭配 opens to 实现完整访问链
真实项目中,往往需要两者协同:
- 对外暴露 API 接口类 →
exports com.example.api to spring.core; - 允许 Spring 反射注入私有字段 →
opens com.example.entity to spring.beans; - 允许 Jackson 序列化内部状态 →
opens com.example.dto to com.fasterxml.jackson.databind;
这样既守住封装边界,又让框架各司其职,不扩大攻击面,也不牺牲灵活性。

















