核心是将可变与不变分离,通过独立common模块封装通用工具、异常结构、基类及领域无关接口,严禁引入框架注解;采用接口+实现分离共享数据访问能力,并用parent pom统一版本与插件管理,严守模块边界。

在多模块 Java 项目中复用公共代码逻辑,核心是把“可变的”和“不变的”分离清楚——业务契约与技术实现解耦,通用能力下沉到独立模块,再按需注入或继承。不是简单地把工具类复制粘贴,而是通过分层设计、依赖策略和接口抽象让复用可持续、不污染、易升级。
抽离独立的 common 模块
新建一个 common(或 core、utils)子模块,只放真正跨模块通用的代码:
- 基础工具类:日期格式化、JSON 转换、字符串校验、加密解密等
- 统一异常结构:
BaseException、ErrorResponse等标准返回封装 - 通用实体基类:
BaseEntity(含id、createTime、updateTime) - 领域无关的接口定义:如
PageResult<t></t>、Result<t></t>
注意:该模块不能依赖任何具体框架(如 Spring MVC、MyBatis、JPA),否则会把技术绑定带到所有引用它的模块中。
用接口 + 实现分离的方式共享数据访问能力
如果多个模块都需要操作同一种业务数据(比如定时任务),不要直接暴露 MyBatis 的 @Mapper 接口,而应:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 在 api 或 repository 包里定义纯接口:
ScheduledTaskRepository,不含任何注解 - 在 impl.mybatis 子包中提供具体实现:
MybatisScheduledTaskRepository,并加上@Mapper - 通过 Spring 条件装配(
@ConditionalOnClass(SqlSessionFactory.class))控制该实现是否生效
这样 JPA 模块可以引入同一套 ScheduledTaskRepository 接口,自行实现 JpaScheduledTaskRepository,互不影响。
通过 parent pom 统一管理版本与插件
父模块的 pom.xml 是复用的“指挥中心”,关键配置包括:
- 打包类型设为
pom,不参与编译 - 用
<dependencymanagement></dependencymanagement>锁定所有子模块共用的依赖版本(如 Spring Boot、Lombok、Jackson) - 用
<pluginmanagement></pluginmanagement>统一 Maven 插件行为(如maven-compiler-plugin的 JDK 版本、编码格式) - 在
<modules></modules>中声明子模块顺序,确保构建时依赖关系正确
避免常见复用陷阱
这些看似方便的做法,实际会破坏模块边界、引发隐式耦合:
- 在 common 模块中引入
@RestController或@Service—— 这会让所有引用它的模块被迫加载 Spring Web 或事务支持 - 把数据库实体类(
@Table、@Id)放在 common 里 —— 不同持久化方案(MyBatis vs JPA)对注解语义理解不同,容易出错 - 子模块直接
import另一个子模块的impl包 —— 打破了分层约定,导致无法替换底层实现 - 用
compileOnly或provided引入 common,却忘了在运行时显式添加 —— 导致 ClassNotFound
复用的本质不是“抄一份”,而是“定义好谁做什么、谁调用谁、谁负责实现”。只要接口稳定、职责清晰、依赖受控,多模块间的代码复用就能既灵活又可靠。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










