java模块化核心是通过module-info.java显式定义“提供什么、需要什么、允许谁用”,按业务域而非技术层划分模块,严格控制exports/opens,单向依赖且禁循环,用requires transitive和spi实现解耦与分层。

设计清晰的模块边界与分层,核心在于职责明确、依赖单向、导出克制。Java 9 的 JPMS 不是简单加个 module-info.java,而是用显式契约倒逼架构思考——每个模块必须回答三个问题:我提供什么?我需要什么?我允许谁用我的哪部分?
按业务能力划分模块,而非技术分层
避免照搬传统“controller-service-dao”垂直切分到每个模块里。应以真实业务域为单位建模:
-
用户中心模块:封装用户注册、认证、资料管理等完整能力,导出
com.example.user.api,内部实现包(如.impl、.repo)不导出 -
订单中心模块:只依赖
user.api(通过requires com.example.user.api),调用用户 ID 查询接口,但不访问用户数据库实体或 Spring 配置 - 网关模块:仅导出统一入口类,依赖各中心 API 模块,不包含任何业务逻辑实现
这种划分让团队能独立演进模块,也天然支持未来拆分为微服务。
用 exports 和 opens 精确控制可见性
导出不是“选包”,而是“选契约”:
- 只
exports接口包(如com.example.order.api),绝不导出.internal、.config或.model(除非模型需被 JSON 序列化且未用 Jackson 模块) - 若需反射(如 Spring Bean 注入、JPA 实体扫描),用
opens com.example.order.model to java.persistence, spring.beans,而非无差别open整个包 - 对测试友好:可单独
opens … to org.junit.platform.commons,生产环境不开放
依赖关系必须单向、无环,善用 requires transitive
防止隐式耦合和依赖泄露:
- 基础工具模块(如
com.example.common)若封装了日志、校验等通用能力,且希望使用者直接用其日志 API,则声明requires transitive java.logging—— 这样上层模块无需重复写requires java.logging - 禁止循环依赖:A
requiresB,B 就不能requiresA;可通过引入第三个api模块解耦(如 A 和 B 都只requires com.example.shared.api) - 第三方库尽量走
requires而非自动模块;若必须用老 JAR,优先在META-INF/MANIFEST.MF中设置Automatic-Module-Name,获得稳定模块名
分层体现在模块间引用关系,而非包名前缀
真正的分层是“谁依赖谁”,不是“谁叫什么”:
- 定义清晰的抽象层模块(如
com.example.storage.spi),只含接口和注解;实现模块(com.example.storage.jdbc)requires它并provides具体实现 - 应用模块(
com.example.app)只requiresSPI 模块和配置模块,运行时由ServiceLoader动态加载具体实现 —— 编译期不绑定 JDBC 细节 - 启动模块(
com.example.boot)负责组装,它requires所有业务模块和框架模块,但自身不导出任何包
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











