模块边界必须由module-info.java显式定义,它是jpms的强制入口和“身份证”;未exports的包对外完全不可见,连反射也不行;opens仅解禁运行时反射,不开放编译访问;依赖需精准声明,避免滥用requires transitive。

模块边界必须由 module-info.java 显式定义
Java 模块系统(JPMS)不依赖隐式的类路径,所有访问控制起点都是 module-info.java 文件。它不是可选项,而是模块的“身份证”和“准入说明书”。没有它,代码就只是传统类路径下的普通包,无法享受模块级封装保护。
关键点在于:未在 exports 中声明的包,无论是否用 public 修饰内部类或方法,对外部模块都完全不可见——反射也无法穿透。这种强封装是 Java 9 之后才具备的硬性隔离能力。
精确导出接口,拒绝宽泛暴露
不要写 exports com.example.service; 这种粗粒度声明。应只导出真正需要被其他模块调用的 API 包,例如:
-
exports com.example.api to com.trusted.client;—— 仅允许指定模块访问 -
exports com.example.dto;—— 导出数据传输对象,但不导出实现类所在的com.example.impl - 内部工具类、配置类、测试辅助类等一律不导出
导出范围越小,攻击面越窄。一个模块里导出多个包,往往意味着职责不清或封装失败。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
用 opens 控制反射访问,而非开放全部
某些框架(如序列化、ORM、测试工具)需要反射访问私有成员。这时不能靠降低访问权限(比如把 private 改成 public),而应使用 opens 指令精准授权:
-
opens com.example.config;—— 允许任意模块通过反射读写该包内类的私有字段 -
opens com.example.model to com.fasterxml.jackson.databind;—— 仅授权 Jackson 模块反射访问模型类
注意:opens 不等于 exports。它不开放编译期访问,只解禁运行时反射限制,安全边界依然清晰。
依赖声明要具体,避免 requires transitive 滥用
requires 表示“我需要你”,requires transitive 表示“我需要你,且我的使用者也间接需要你”。后者会将依赖传递出去,扩大信任链。
- 除非你的模块明确充当“桥接层”(如提供统一日志门面),否则避免使用 transitive
- 对敏感模块(如加密、权限校验)应严格控制谁可以 requires 它,必要时配合 qualified exports 限定调用方
- 编译时加
--limit-modules可进一步约束可解析模块集,防止意外依赖混入
模块系统的严密性,不来自某一行代码,而来自每一处 module-info.java 的审慎决策。它让“谁能看到什么”变成编译期可验证的事实,而不是靠开发者自觉遵守的约定。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










