SpringBoot 多模块项目搭建:service/dao/web分层设计

在实际企业开发中,简单的单模块 SpringBoot 项目只能用来快速 Dem)o 或小型应用,一旦业务复杂、团队人数变多、需求迭代加快,单模块就会暴露出代码混乱、职责不清、难以维护、编译打包慢、不利于后期微服务拆分等一系列问题。

SpringBoot 多模块架构 ,正是解决这些问题的标准方案。它不仅仅是把代码拆成几个文件夹,而是真正意义上的工程化、规范化、分层解耦,也是后端开发者从"会写代码"走向"会做架构"的重要一步。


一、为什么必须使用多模块项目?

多模块不是为了复杂而复杂,而是为了工程更健康:

    1. 职责单一,高内聚低耦合
  • • Controller 只负责接收请求、返回结果

  • • Service 只负责业务逻辑、事务、流程编排

  • • Dao 只负责数据库交互

  • • 每层只依赖下层,不跨层调用

    1. 代码复用性极强
    • • 工具类、常量、通用配置放在 common

    • • 实体类统一放在 pojo,全工程共享

    • • 避免重复代码,减少 Bug

    1. 编译速度大幅提升
    • • 修改单个模块只打包当前模块

    • • 大型项目节省大量时间

    1. 便于团队协作开发
    • • 前端只对接 web 模块

    • • 业务开发专注 service

    • • DBA 关注 dao

    • • 互不干扰、权限清晰

    1. 平滑过渡微服务
    • • 多模块是微服务的"前身"

    • • 未来业务膨胀,可直接把模块独立成微服务

    1. 结构规范,新人快速上手
    • • 统一包结构、统一配置、统一规范

    • • 降低团队沟通与维护成本


    二、标准多模块结构设计

    最经典、最通用、最稳定的结构如下:

    go 复制代码
    springboot-multi-module
    ├── pom.xml                             # 父工程:统一版本、依赖管理
    ├── module-common                       # 公共模块:工具、异常、配置、常量
    ├── module-pojo                         # 实体模块:entity/dto/vo/bo
    ├── module-dao                          # 数据访问层:mapper、repository、xml
    ├── module-service                      # 业务逻辑层:service接口、实现、事务
    └── module-web                          # 接口层:controller、拦截器、启动类

    各模块职责说明

      1. module-common
    • • 全局工具类:DateUtil、StringUtil、BeanUtil

    • • 全局异常处理、统一返回封装

    • • 全局配置类:Redis、MyBatis、线程池

    • • 常量、枚举、公共注解

    1. module-pojo
    • • Entity:数据库映射实体

    • • DTO:前端传入参数封装

    • • VO:返回前端视图对象

    • • BO:业务层内部传递对象

    • • Query:分页查询条件

    1. module-dao
    • • Mapper 接口(MyBatis/MyBatis-Plus)

    • • Repository(JPA、ES、MongoDB)

    • • Mapper XML 文件

    • • 数据源相关配置

    1. module-service
    • • Service 接口

    • • ServiceImpl 业务实现

    • • 事务控制

    • • 缓存逻辑

    • • 第三方接口调用

    • • 内部逻辑编排

    1. module-web
    • • Controller 接口

    • • 全局跨域、过滤器、拦截器

    • • 项目启动类

    • • application.yml / application-prod.yml

    • • Swagger/Knife4j 接口文档


    三、第一步:创建父工程(统一版本管理)

    父工程 packaging = pom,只做依赖管理,不写业务代码。

    父 pom.xml

    go 复制代码
    <?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>
    
        <groupId>com.demo</groupId>
        <artifactId>springboot-multi-module</artifactId>
        <version>1.0.0</version>
        <packaging>pom</packaging>
        <name>springboot-multi-module</name>
    
        <!-- 子模块声明 -->
        <modules>
            <module>module-common</module>
            <module>module-pojo</module>
            <module>module-dao</module>
            <module>module-service</module>
            <module>module-web</module>
        </modules>
    
        <!-- 统一版本 -->
        <properties>
            <java.version>1.8</java.version>
            <spring.boot.version>2.7.18</spring.boot.version>
            <mybatis.plus.version>3.5.3.1</mybatis.plus.version>
            <lombok.version>1.18.30</lombok.version>
        </properties>
    
        <!-- 全局依赖版本管理 -->
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>${spring.boot.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
    
                <dependency>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                    <version>${mybatis.plus.version}</version>
                </dependency>
    
                <dependency>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                    <version>${lombok.version}</version>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
    </project>

    四、第二步:逐个创建子模块

    1. module-pojo 实体模块

    所有数据结构统一存放,不依赖任何业务模块,只依赖 lombok。

    pom.xml

    go 复制代码
    <parent>
        <groupId>com.demo</groupId>
        <artifactId>springboot-multi-module</artifactId>
        <version>1.0.0</version>
    </parent>
    
    <artifactId>module-pojo</artifactId>
    
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    示例 User.java

    go 复制代码
    @Data
    @TableName("t_user")
    public class User {
        @TableId(type = IdType.AUTO)
        private Long id;
        private String username;
        private String password;
        private Integer status;
        private LocalDateTime createTime;
    }

    UserVO.java

    go 复制代码
    @Data
    public class UserVO {
        private Long id;
        private String username;
        private Integer status;
    }

    2. module-common 公共模块

    依赖:pojo

    go 复制代码
    <parent>
        <groupId>com.demo</groupId>
        <artifactId>springboot-multi-module</artifactId>
        <version>1.0.0</version>
    </parent>
    
    <artifactId>module-common</artifactId>
    
    <dependencies>
        <dependency>
            <groupId>com.demo</groupId>
            <artifactId>module-pojo</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.25</version>
        </dependency>
    </dependencies>

    统一返回 Result

    go 复制代码
    @Data
    public class Result<T> {
        private Integer code;
        private String msg;
        private T data;
    
        public static <T> Result<T> success(T data) {
            Result<T> r = new Result<>();
            r.setCode(200);
            r.setMsg("success");
            r.setData(data);
            return r;
        }
        public static <T> Result<T> fail(String msg) {
            Result<T> r = new Result<>();
            r.setCode(500);
            r.setMsg(msg);
            return r;
        }
    }

    3. module-dao 数据访问层

    依赖:pojo + common + mybatis-plus

    go 复制代码
    <parent>
        <groupId>com.demo</groupId>
        <artifactId>springboot-multi-module</artifactId>
        <version>1.0.0</version>
    </parent>
    
    <artifactId>module-dao</artifactId>
    
    <dependencies>
        <dependency>
            <groupId>com.demo</groupId>
            <artifactId>module-pojo</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.demo</groupId>
            <artifactId>module-common</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
    
    <!-- 打包 xml -->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.xml</include>
                    <include>**/*.yml</include>
                </includes>
            </resource>
        </resources>
    </build>

    UserMapper.java

    go 复制代码
    public interface UserMapper extends BaseMapper<User> {
    }

    4. module-service 业务层

    依赖:dao + common + pojo

    go 复制代码
    <parent>
        <groupId>com.demo</groupId>
        <artifactId>springboot-multi-module</artifactId>
        <version>1.0.0</version>
    </parent>
    
    <artifactId>module-service</artifactId>
    
    <dependencies>
        <dependency>
            <groupId>com.demo</groupId>
            <artifactId>module-dao</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.demo</groupId>
            <artifactId>module-common</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>

    UserService.java

    go 复制代码
    public interface UserService {
        UserVO getUserById(Long id);
    }

    UserServiceImpl.java

    go 复制代码
    @Service
    public class UserServiceImpl implements UserService {
    
        @Autowired
        private UserMapper userMapper;
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public UserVO getUserById(Long id) {
            User user = userMapper.selectById(id);
            if (user == null) {
                throw new RuntimeException("用户不存在");
            }
            return BeanUtil.copyProperties(user, UserVO.class);
        }
    }

    5. module-web 接口层(启动模块)

    依赖:service

    go 复制代码
    <parent>
        <groupId>com.demo</groupId>
        <artifactId>springboot-multi-module</artifactId>
        <version>1.0.0</version>
    </parent>
    
    <artifactId>module-web</artifactId>
    
    <dependencies>
        <dependency>
            <groupId>com.demo</groupId>
            <artifactId>module-service</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    application.yml

    go 复制代码
    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/demo?useSSL=false
        username: root
        password: root
    
    mybatis-plus:
      mapper-locations: classpath:mapper/*.xml
      type-aliases-package: com.demo.pojo

    启动类

    go 复制代码
    @SpringBootApplication(scanBasePackages = "com.demo")
    @MapperScan("com.demo.dao.mapper")
    public class WebApplication {
        public static void main(String[] args) {
            SpringApplication.run(WebApplication.class, args);
        }
    }

    Controller

    go 复制代码
    @RestController
    @RequestMapping("/user")
    public class UserController {
    
        @Autowired
        private UserService userService;
    
        @GetMapping("/{id}")
        public Result<UserVO> getUser(@PathVariable Long id) {
            return Result.success(userService.getUserById(id));
        }
    }

    五、模块依赖关系

    go 复制代码
    web → service
    service → dao
    dao → pojo, common
    所有模块 → common

    禁止:

    • • web 直接调用 dao

    • • dao 调用 service

    • • pojo 依赖任何业务模块

    • • 循环依赖 A↔B


    六、分层设计原则

      1. Controller 只做三件事
    • • 接收参数

    • • 调用 service

    • • 返回统一结果

    1. Service 是业务核心
    • • 事务控制

    • • 业务判断

    • • 多表操作

    • • 缓存逻辑

    1. Dao 只做数据读写
    • • 不写业务

    • • 不做判断

    • • 只提供数据能力

    1. Pojo 无任何逻辑
    • • 只有属性、get/set

    七、项目打包与运行

    go 复制代码
    # 父工程执行
    mvn clean package -DskipTests
    • • 可执行 jar 仅出现在 module-web/target

    • • 运行:

    go 复制代码
    java -jar module-web-1.0.0.jar

    八、注意事项

      1. Mapper 找不到 / Invalid bound statement
    • • dao 模块未配置 resource 打包 xml

    • • @MapperScan 路径错误

    1. 启动类无法扫描其他模块
    • • 添加 scanBasePackages = "com.demo"
    1. 依赖冲突
    • • 父工程使用 dependencyManagement 统一管控
    1. 循环依赖
    • • 抽取公共部分到 common

    • • 重新设计分层

    1. 配置文件不生效
    • • 配置文件必须放在 web 模块 resources

    九、总结

    SpringBoot 多模块 + MVC 三层架构(web/service/dao)是企业后端最标准、最通用、最稳健的工程结构。

    它的核心价值是:职责清晰、分层解耦、易于维护、易于扩展、便于团队协作

    只要按照这套结构搭建项目,你的工程规范度、可维护性、可扩展性都会直接达到企业级标准。

    掌握多模块搭建,是你从普通程序员迈向架构思维的关键一步。


    你们公司项目是单模块还是多模块?有没有遇到过分层混乱、依赖冲突、启动报错的问题?

    欢迎在评论区留言 交流你的经验和踩坑经历!

    关注我,持续更新 SpringBoot 企业级实战教程,从工程搭建到微服务、高并发、分布式架构,带你一步步成长为后端大牛!

相关推荐
星晨雪海2 小时前
springboot 增删改查全套流程
java·spring boot·spring
Devin~Y2 小时前
高并发内容社区实战面试:从 Java 基础到 Spring Cloud、Kafka、Redis、RAG 搜索全解析
java·spring boot·redis·spring cloud·kafka·向量数据库·rag
C雨后彩虹2 小时前
箱子之字形摆放
java·数据结构·算法·华为·面试
Victor3562 小时前
MongoDB(86)如何使用MongoDB存储大文件?
后端
cch89182 小时前
ThinkPHP3.x核心特性全解析
开发语言·后端·golang
Victor3562 小时前
MongoDB(85)如何实现全文搜索?
后端
star-yp2 小时前
vibe coding 博客管理系统
java·spring boot·spring·ai·ai编程
小江的记录本2 小时前
【JEECG Boot】JEECG Boot 系统性知识体系全方位结构化总结
java·前端·spring boot·后端·python·spring·spring cloud
Mr.wangh2 小时前
Spring原理(Bean的生命周期)
java·前端·spring