模块化重构关键在划边界、控依赖、收出口:先冻结exports暴露耦合点,再按需导出API接口而非实现,接着用opens/uses/provides替代硬绑定,最后结合分层与依赖倒置加固架构。

老旧 Java 项目做模块化重构,不是一上来就改 module-info.java,而是借模块化倒逼结构理清——把隐性依赖显性暴露出来,再一层层解耦。关键不在“加模块”,而在“划边界、控依赖、收出口”。
第一步:冻结 exports,暴露真实耦合点
给每个旧模块补最简 module-info.java,只写模块名和 requires java.base,所有 exports 先注释掉。此时编译必然失败,而报错位置就是被外部直接引用的包——这些就是当前实际存在的耦合入口。
- 用 IDE 的 “Find Usages” 查看哪些模块 import 了你的某个包
- 重点关注跨模块的
new XXXService()、字段类型声明(如private PaymentProcessor processor)、静态工具类调用 - 对报错处不做绕行修复,而是记下来,留作第二步收敛依据
第二步:按需导出,只放接口不放实现
不再“整个包导出”,而是只对真正被依赖的 API 包做 exports;内部实现包一律不导出,哪怕暂时编译不过,也要推动调用方改用接口。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 把跨模块使用的类提取为接口,放到独立的
api模块(如com.example.order.api) - 原 service 类改为
package-private或final,仅保留接口供外部依赖 - 字段类型、构造器参数、方法返回值全部替换为接口类型,杜绝具体类跨模块传递
第三步:用 opens / uses / provides 替代硬绑定
反射、SPI、配置加载等场景不能靠导出整个包来解决,否则又回到强耦合。应转向模块系统提供的轻量级契约机制。
立即学习“Java免费学习笔记(深入)”;
- 需要反射访问内部类?用
opens com.example.internal to com.config.loader精准授权 - 日志、序列化、规则引擎等扩展点?改用
uses com.example.spi.Logger+provides实现类声明 - 避免自动模块(老 JAR)被多个具名模块 require 后形成隐式反向依赖,可引入桥接模块统一适配
第四步:配合分层与依赖倒置加固结构
模块化只是手段,目标是让业务逻辑、数据访问、外部集成各守其界。需同步推进架构层面调整。
- 禁止 service 层直接 new DAO 或调用数据库驱动,必须通过接口抽象
- controller 只依赖 service 接口,不感知 infra 实现细节
- 用 Spring 的
@Qualifier或模块级provides绑定具体实现,运行时注入而非编译期强引 - 数据库表按模块分组,物理隔离配合逻辑模块边界

















