Spring Boot多模块Maven项目如何拆分接口与实现到不同模块
核心问题定位
你的依赖配置方向完全错误。接口定义属于上层抽象,实现类属于下层具体逻辑,依赖规则必须是实现模块依赖接口模块。你当前在存放接口的模块A中引入实现模块B的依赖,属于抽象层反向依赖实现层,违背依赖倒置原则,直接导致编译失败、循环依赖等问题。
另外你写的实现类中testMethod()没有加public修饰符:接口方法默认是public访问权限,实现类重写时访问权限不能低于接口定义,否则也会编译报错。
正确多模块搭建示例
整体目录结构
multi-module-demo // 父工程 ├── pom.xml ├── api-module // 原模块A,存放接口定义 │ └── pom.xml └── service-module // 原模块B,存放接口实现 └── pom.xml
如果是Web项目,可以额外加一个start模块作为启动入口,依赖service-module即可。
父工程POM配置
父工程打包方式必须为pom,统一管理依赖版本和子模块声明:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.5</version> <relativePath/> </parent> <groupId>test.some</groupId> <artifactId>multi-module-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <!-- 子模块声明顺序:被依赖的模块放前面,Maven按顺序编译 --> <modules> <module>api-module</module> <module>service-module</module> </modules> <properties> <java.version>17</java.version> </properties> <!-- 统一管理子模块依赖版本,子模块引用时无需写version --> <dependencyManagement> <dependencies> <dependency> <groupId>test.some</groupId> <artifactId>api-module</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>test.some</groupId> <artifactId>service-module</artifactId> <version>${project.version}</version> </dependency> </dependencies> </dependencyManagement> </project>
模块A(api-module 接口层)配置
该模块只存放接口定义,不需要依赖实现模块,POM配置如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>test.some</groupId> <artifactId>multi-module-demo</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>api-module</artifactId> <dependencies> <!-- 纯接口定义不需要额外依赖,用到Spring注解时可按需引入starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> </dependencies> </project>
在该模块下编写接口代码,示例包路径test.some.api:
package test.some.api; public interface MyTestInterface { String testMethod(); }
模块B(service-module 实现层)配置
该模块需要引入接口模块A的依赖,POM配置如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>test.some</groupId> <artifactId>multi-module-demo</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>service-module</artifactId> <dependencies> <!-- 引入接口模块依赖 --> <dependency> <groupId>test.some</groupId> <artifactId>api-module</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> </dependencies> </project>
在该模块下编写实现类,加@Service注解交给Spring容器管理,注意重写方法加public修饰符,示例包路径test.some.service.impl:
package test.some.service.impl; import org.springframework.stereotype.Service; import test.some.api.MyTestInterface; @Service public class MyTestImpl implements MyTestInterface { @Override public String testMethod() { return "foo"; } }
注意:如果Spring Boot启动类放在其他独立模块,需要配置包扫描路径覆盖实现类所在包,否则容器无法注入实现类Bean,可在启动类上添加注解
@SpringBootApplication(scanBasePackages = "test.some")解决。
接口与实现分模块设计的合理性说明
- 这是非常成熟的通用工程实践,本质是面向接口编程思想在工程结构上的落地,完全符合依赖倒置原则。
- 核心优势:对外提供能力时只需要暴露接口包,不泄露实现逻辑;多实现场景下可以灵活替换实现模块,不需要修改上层接口代码;模块职责边界清晰,接口层作为稳定抽象,实现层可以独立迭代不影响上层调用。
- 适用边界:如果是极小的单体项目,没有对外复用、多实现切换的需求,强行拆分模块会增加工程复杂度,没有必要。
内容的提问来源于stack exchange,提问作者sras
相关产品推荐
相关产品推荐

