Java JPMS通过模块化机制在编译期和加载期强制隔离包可见性,仅exports必要API、opens反射入口给指定模块、requires声明依赖即权限契约,彻底替代运行时权限校验与脆弱的字段保护逻辑。

直接用模块化机制控制包可见性,比在代码里写一堆权限判断和变量保护逻辑更干净、更可靠。Java 的 JPMS(Java Platform Module System)从语言层面就做了这件事——不是靠运行时检查,而是靠编译期和加载期的强制隔离。
只导出真正需要暴露的包
把业务接口和实现彻底分开,是第一步。模块默认不导出任何包,所以你得主动声明哪些能被别人用:
- 在
module-info.java中仅exports com.example.api;,而把com.example.internal、com.example.config这类包完全隐藏 - 外部模块即使通过反射尝试访问
internal包下的类,也会在运行时报IllegalAccessError,无需你手动 throw 异常 - 避免使用
exports ... to泛化开放,除非明确知道调用方是谁
用 opens 精准放开反射入口
有些场景绕不开反射(比如框架注入、序列化),但不能全盘放开。JPMS 提供了细粒度控制:
-
opens com.example.config to spring.core;表示只允许spring.core模块在运行时通过反射读写该包 - 其他模块哪怕硬编码反射调用,也会被 JVM 拦截,不会进入你的校验逻辑分支
- 比起在每个 setter 方法里加
if (!callerIsTrusted()) throw new SecurityException(),这一步省掉了全部运行时开销
依赖声明即权限契约
模块间的 requires 不只是“我要用它”,更是“我被允许用它”:
- 如果 A 模块没声明
requires B,哪怕 B 在 classpath 上,A 也根本看不到 B 的导出包 - 第三方库若想访问你的敏感能力,必须先出现在你的
requires列表中——这本身就是一道编译期准入门槛 - 不需要再写类似
SecurityManager.checkPermission(new RuntimePermission("accessClassInPackage.com.example.internal"))这种已过时且低效的逻辑
替代传统变量保护的封装实践
字段级保护也可以前移到模块设计层,减少 getter/setter 中的条件判断:
- 把敏感字段放在非导出包内(如
com.example.auth.impl.TokenStore),外部模块连类名都看不到 - 对外只提供不可变接口(
TokenView),由内部模块负责构造和更新,外部只能读 - 不需要在每个 setter 里加
if (Thread.currentThread().getName().contains("audit"))这类脆弱的线程名校验










