模块化设计需通过module-info.java强制约束:按业务能力划分模块、精确exports接口包、requires最小依赖、uses/provides实现spi扩展。

设计清晰的模块边界,核心是让每个模块只暴露它真正需要被外部使用的契约,同时严格隐藏实现细节。这不靠命名约定或文档约束,而由 module-info.java 在编译期强制执行。
明确模块职责,一个模块只做一件事
模块不是按技术分层(如“controller”“service”),而是按业务能力或抽象维度划分。例如:
-
app.view:只定义 UI 抽象接口(如
View、Renderer),不包含任何 Swing 或 JavaFX 实现 -
app.engine:只声明核心计算契约(如
Calculator、RuleEvaluator),不耦合数据源或日志框架 -
app.impl.swing:仅实现
app.view接口,且只依赖app.view和java.desktop
用 exports 精确控制可见范围
exports 不是“开放所有 public 类”,而是指定哪些包对外可访问。未导出的包,即使 public,其他模块也编译不过。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 导出接口所在的包,比如
exports app.view.api; - 绝不导出含实现类、工具类或内部模型的包(如
app.view.internal) - 避免导出整个模块名路径(如
exports app.view;),除非该包下全是 SPI 接口
依赖声明必须显式且最小化
每个 requires 都应有明确用途,禁止“为保险起见”多加依赖。
- 只
requires当前模块运行真正需要的模块(如app.view只需java.base,不需要java.logging) - 若某 API 方法返回了来自依赖模块的类型(如返回
java.time.LocalDate),无需额外requires java.time——它是java.base的一部分 - 对第三方库(如 SLF4J)使用
requires static仅用于编译,避免运行时强绑定
用 uses 和 provides 支撑 SPI 扩展机制
SPI 是模块间松耦合的关键,但必须配合正确的导出策略:
- 定义 SPI 的模块(如
app.view)要exports接口包,并保持开放(不用to限定) - 主应用模块(
app)在module-info.java中写uses app.view.View;表明消费该服务 - 扩展模块(如
my.custom.view)写provides app.view.View with MyViewImpl;并requires app.view;
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










