精准控制敏感变量包访问范围需依托模块系统的限定导出:exports仅授权目标模块使用public类和成员,配合包私有/ private 修饰符保障字段安全,opens专用于反射且须限定范围,非public类天然隔离。
精准控制敏感变量包的访问范围,关键在于利用模块系统提供的“限定导出”能力,而非仅依赖访问修饰符或命名约定。核心不是“能不能看到”,而是“谁被明确允许使用”。以 java 9+ 的 jpms(模块系统)为例,exports 本身不开放私有成员,也不赋予反射权限;它只决定:其他模块能否在编译和运行时直接引用你包里的 public 类和 public 成员。
明确指定目标模块,拒绝泛化暴露
避免写 exports com.example.data; 这类无约束声明——它等于向所有模块敞开大门。应始终用 to 子句锁定可信消费者:
-
exports com.example.secrets to com.example.reporter;→ 仅 reporter 模块可 new User()、调用 getUserToken() -
exports com.example.config to spring.core, org.junit.jupiter.api;→ 仅 Spring 和 JUnit 可加载该包内 public 配置类 - 未列入
to列表的模块,即使有 public 类,也会在编译时报package com.example.secrets is not visible
敏感字段必须配合包私有或 private 修饰
exports 不改变字段本身的访问级别。哪怕你导出了整个包,以下情况仍绝对安全:
- 一个
public class TokenHolder { String rawKey; }——rawKey是包私有字段,其他模块无法访问,哪怕已exports com.example.secrets to ... - 一个
public class Credential { private String password; }——password是 private 字段,外部模块连编译都过不去 - 真正可被跨模块读写的,仅限于
public字段 +publicgetter/setter + 所在包已被exports ... to显式授权
区分 exports 与 opens:绝不混淆用途
若第三方框架(如 Hibernate、Jackson)需反射操作敏感字段,请单独使用 opens,且严格限定范围:
-
opens com.example.secrets to com.fasterxml.jackson.databind;→ 允许 Jackson 反射读写private String token;,但 reporter 模块依然不能直接调用token -
opens com.example.secrets;(无to)→ 危险!所有模块均可反射访问,违背最小权限原则 -
exports控制“正常调用”,opens控制“反射穿透”,二者不可互相替代,也不应合并声明
包私有类天然免疫跨模块访问
即使你误写了 exports com.example.secrets to everything;,只要类本身没加 public,就完全不会泄露:
-
class InternalSecretManager { ... }→ 无public修饰符,其他模块根本看不到这个类名,更别说实例化或调用 - 这意味着:把敏感逻辑封装在非 public 类中,比单纯靠
exports更底层、更可靠 - 推荐模式:
public interface SecretProvider导出供调用,class DefaultSecretProvider包私有实现,仅通过工厂或服务定位器返回

















