SpringBoot聚合模块搭建指南
前言
在现代微服务架构中,模块化开发已成为主流。SpringBoot聚合模块(Maven Multi-Module Project)允许我们将一个大型项目拆分成多个独立的模块,每个模块负责特定的功能,既便于管理又有利于团队协作。本文将详细介绍如何搭建一个完整的SpringBoot聚合模块项目。
什么是SpringBoot聚合模块
SpringBoot聚合模块是基于Maven多模块项目结构的一种项目组织方式。它允许我们:
- 将复杂的单体应用拆分为多个子模块
- 实现模块间的依赖管理
- 提供统一的构建和部署流程
- 促进代码复用和团队协作
项目结构设计
一个典型的SpringBoot聚合模块项目结构如下:
my-project/
├── pom.xml # 父模块POM
├── my-project-common/ # 公共模块
│ ├── pom.xml
│ └── src/
├── my-project-dao/ # 数据访问层
│ ├── pom.xml
│ └── src/
├── my-project-service/ # 业务逻辑层
│ ├── pom.xml
│ └── src/
├── my-project-web/ # Web层
│ ├── pom.xml
│ └── src/
└── my-project-app/ # 启动模块
├── pom.xml
└── src/
搭建步骤
1. 创建父模块
首先创建父模块的pom.xml文件:
<?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.example</groupId>
<artifactId>my-project</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<name>my-project</name>
<description>SpringBoot聚合模块项目</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mysql.version>8.0.33</mysql.version>
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
</properties>
<modules>
<module>my-project-common</module>
<module>my-project-dao</module>
<module>my-project-service</module>
<module>my-project-web</module>
<module>my-project-app</module>
</modules>
<dependencyManagement>
<dependencies>
<!-- 子模块依赖 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>my-project-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-project-dao</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-project-service</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-project-web</artifactId>
<version>${project.version}</version>
</dependency>
<!-- 第三方依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2. 创建公共模块(Common)
my-project-common/pom.xml:
<?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>com.example</groupId>
<artifactId>my-project</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>my-project-common</artifactId>
<name>my-project-common</name>
<description>公共模块</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
</dependencies>
</project>
公共模块的响应结果类:
// src/main/java/com/example/common/result/Result.java
package com.example.common.result;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.io.Serializable;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Result<T> implements Serializable {
private Integer code;
private String message;
private T data;
public Result() {}
public Result(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static <T> Result<T> success() {
return new Result<>(200, "操作成功", null);
}
public static <T> Result<T> success(T data) {
return new Result<>(200, "操作成功", data);
}
public static <T> Result<T> error(String message) {
return new Result<>(500, message, null);
}
public static <T> Result<T> error(Integer code, String message) {
return new Result<>(code, message, null);
}
// Getters and Setters
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
3. 创建数据访问层(DAO)
my-project-dao/pom.xml:
<?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>com.example</groupId>
<artifactId>my-project</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>my-project-dao</artifactId>
<name>my-project-dao</name>
<description>数据访问层</description>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-project-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>
实体类示例:
// src/main/java/com/example/dao/entity/User.java
package com.example.dao.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.time.LocalDateTime;
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String email;
private LocalDateTime createTime;
private LocalDateTime updateTime;
// Constructors, Getters and Setters
public User() {}
public User(String username, String email) {
this.username = username;
this.email = email;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
}
Mapper接口:
// src/main/java/com/example/dao/mapper/UserMapper.java
package com.example.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.dao.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 可以添加自定义查询方法
}
4. 创建业务逻辑层(Service)
my-project-service/pom.xml:
<?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>com.example</groupId>
<artifactId>my-project</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>my-project-service</artifactId>
<name>my-project-service</name>
<description>业务逻辑层</description>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-project-dao</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
业务逻辑实现:
// src/main/java/com/example/service/UserService.java
package com.example.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.dao.entity.User;
import java.util.List;
public interface UserService extends IService<User> {
List<User> findByUsername(String username);
User createUser(String username, String email);
}
// src/main/java/com/example/service/impl/UserServiceImpl.java
package com.example.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.dao.entity.User;
import com.example.dao.mapper.UserMapper;
import com.example.service.UserService;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> findByUsername(String username) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", username);
return list(queryWrapper);
}
@Override
public User createUser(String username, String email) {
User user = new User(username, email);
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
save(user);
return user;
}
}
5. 创建Web层
my-project-web/pom.xml:
<?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>com.example</groupId>
<artifactId>my-project</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>my-project-web</artifactId>
<name>my-project-web</name>
<description>Web层</description>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-project-service</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
</project>
控制器实现:
// src/main/java/com/example/web/controller/UserController.java
package com.example.web.controller;
import com.example.common.result.Result;
import com.example.dao.entity.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public Result<List<User>> getAllUsers() {
List<User> users = userService.list();
return Result.success(users);
}
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable Long id) {
User user = userService.getById(id);
if (user != null) {
return Result.success(user);
}
return Result.error("用户不存在");
}
@PostMapping
public Result<User> createUser(@Valid @RequestBody CreateUserRequest request) {
User user = userService.createUser(request.getUsername(), request.getEmail());
return Result.success(user);
}
@PutMapping("/{id}")
public Result<User> updateUser(@PathVariable Long id, @Valid @RequestBody UpdateUserRequest request) {
User user = userService.getById(id);
if (user == null) {
return Result.error("用户不存在");
}
user.setUsername(request.getUsername());
user.setEmail(request.getEmail());
userService.updateById(user);
return Result.success(user);
}
@DeleteMapping("/{id}")
public Result<Void> deleteUser(@PathVariable Long id) {
boolean success = userService.removeById(id);
if (success) {
return Result.success();
}
return Result.error("删除失败");
}
}
请求DTO:
// src/main/java/com/example/web/dto/CreateUserRequest.java
package com.example.web.dto;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
public class CreateUserRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
private String username;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
// Getters and Setters
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
6. 创建启动模块(App)
my-project-app/pom.xml:
<?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>com.example</groupId>
<artifactId>my-project</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>my-project-app</artifactId>
<name>my-project-app</name>
<description>启动模块</description>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-project-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
启动类:
// src/main/java/com/example/MyProjectApplication.java
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyProjectApplication {
public static void main(String[] args) {
SpringApplication.run(MyProjectApplication.class, args);
}
}
配置文件 application.yml:
# src/main/resources/application.yml
server:
port: 8080
servlet:
context-path: /
spring:
datasource:
url: jdbc:mysql://localhost:3306/myproject?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
format_sql: true
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath*:mapper/*.xml
logging:
level:
com.example: DEBUG
org.springframework.web: DEBUG
构建和运行
1. 构建项目
在项目根目录执行:
mvn clean install
2. 运行应用
cd my-project-app
mvn spring-boot:run
或者:
java -jar my-project-app/target/my-project-app-1.0.0.jar
最佳实践
1. 模块设计原则
- 单一职责原则:每个模块只负责一个特定的功能域
- 低耦合高内聚:模块间依赖关系清晰,内部功能紧密相关
- 依赖方向:遵循分层架构,避免循环依赖
2. 依赖管理
- 在父模块中使用
<dependencyManagement>统一管理版本 - 子模块只需声明依赖,不需要指定版本
- 合理使用
<scope>控制依赖范围
3. 配置管理
- 将配置文件放在启动模块中
- 使用Spring Profile管理不同环境的配置
- 敏感信息使用环境变量或配置中心
4. 测试策略
- 单元测试:在各个模块中编写针对性的单元测试
- 集成测试:在启动模块中编写端到端测试
- 使用Spring Boot Test进行测试
5. 文档和规范
- 编写清晰的README文档
- 统一代码规范和注释风格
- 使用Swagger生成API文档
常见问题解决
1. 循环依赖问题
如果出现循环依赖,可以通过以下方式解决:
- 重新设计模块结构
- 将公共接口提取到独立模块
- 使用Spring的
@Lazy注解
2. 包扫描问题
确保启动类能够扫描到所有需要的包:
@SpringBootApplication(scanBasePackages = "com.example")
public class MyProjectApplication {
// ...
}
3. 资源文件问题
确保资源文件路径正确,可以使用Maven的资源过滤功能:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
总结
SpringBoot聚合模块搭建是一个复杂但非常有价值的工程实践。通过合理的模块设计和依赖管理,我们可以构建出易于维护、扩展和测试的应用程序。
关键要点:
- 遵循分层架构原则
- 合理设计模块依赖关系
- 统一管理依赖版本
- 编写全面的测试用例
- 建立完善的文档体系
版本兼容性说明
本文使用的版本配置:
- JDK 1.8 - 企业级应用的主流版本
- SpringBoot 2.7.14 - 与JDK 1.8完全兼容的稳定版本
- MyBatis Plus 3.5.3.1 - 支持JDK 1.8的最新稳定版本
如果您的项目需要使用更高版本的JDK,可以相应调整:
- JDK 11:SpringBoot 2.7.x 或 3.x
- JDK 17:SpringBoot 3.x(推荐)
希望这篇文章能帮助您更好地理解和实践SpringBoot聚合模块的搭建。在实际项目中,可以根据具体需求调整模块结构和配置,关键是保持架构的清晰性和可维护性。