You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 04:24:24