SpringBoot3 登录注册基础篇(一) - 多模块架构与统一响应封装
发表于更新于
字数总计:2.9k阅读时长:14分钟阅读量: 广东
SpringBoot3 登录注册基础篇(一) - 多模块架构与统一响应封装
第一章. 为什么需要多模块架构
在开始构建认证系统之前,我们先解决一个基础问题:为什么不能把所有代码都塞在一个模块里?
假设我们现在有一个单模块项目,所有代码都在 src/main/java/com/example/auth 下。这种结构在项目初期看起来很简洁,但随着业务增长,会遇到三个致命问题。
1.1. 单模块的三大困境
困境一:职责不清
JwtUtil 是通用的 JWT 工具类,理论上可以被其他项目复用。但现在它和业务代码混在一起,如果其他项目想用,只能复制粘贴代码。
困境二:依赖混乱
AuthController 依赖了 TokenService,TokenService 依赖了 JwtUtil,JwtUtil 依赖了 RedisUtil
。这些依赖关系全部隐藏在代码里,新人接手项目时很难理解架构。
困境三:测试困难
如果我想单独测试 JwtUtil 的功能,必须启动整个 Spring Boot 应用,因为它和 Web 层耦合在一起。
1.2. 多模块架构设计
我们将项目拆分成多个模块,每个模块有明确的职责:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| auth/ # 根聚合器 ├── pom.xml # 父POM(统一管理依赖版本) ├── auth-common/ # 公共基础模块 │ └── src/main/java/.../common/ │ ├── model/Result.java │ └── util/SnowflakeIdGenerator.java ├── auth-core/ # 认证核心模块 │ └── src/main/java/.../core/ │ ├── util/JwtUtil.java │ └── service/TokenService.java └── auth-web/ # Web应用模块 └── src/main/java/.../web/ ├── AuthApplication.java └── controller/AuthController.java
|
依赖关系非常清晰:auth-web → auth-core → auth-common
核心设计:聚合器和父 POM 分离,根目录只声明模块,auth-parent 只管理依赖版本。
第二章. 核心配置文件
2.1. 根 POM 配置(聚合器 + 父工程)
根目录的 pom.xml 同时负责两个职责:
- 聚合器(Aggregator):通过
<modules> 声明包含哪些子模块 - 父工程(Parent):通过
<dependencyManagement> 统一管理依赖版本
📄 文件路径:pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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>auth</artifactId> <version>1.0.0</version> <packaging>pom</packaging>
<name>auth</name> <description>Auth System Multi-Module Project</description>
<properties> <java.version>17</java.version> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring-boot.version>3.4.2</spring-boot.version> <jjwt.version>0.12.5</jjwt.version> <hutool.version>5.8.34</hutool.version> <lombok.version>1.18.30</lombok.version> </properties>
<modules> <module>auth-common</module> <module>auth-core</module> <module>auth-web</module> </modules>
<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>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency>
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>${jjwt.version}</version> </dependency>
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>${jjwt.version}</version> </dependency>
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>${jjwt.version}</version> </dependency>
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>${hutool.version}</version> </dependency> </dependencies> </dependencyManagement>
<build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.13.0</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> <encoding>UTF-8</encoding> <parameters>true</parameters> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> </plugin> </plugins> </pluginManagement> </build> </project>
|
标准实践:在 99% 的 Maven 多模块项目中,根目录既是聚合器也是父工程。这样可以:
- 减少目录层级,结构更清晰
- 避免维护多余的 POM 文件
- 简化子模块的
<relativePath> 配置
2.2. 子模块配置
每个子模块都需要指定父 POM 为根目录的 auth,并声明自己的依赖。
auth-common 模块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <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>auth</artifactId> <version>1.0.0</version> <relativePath>../pom.xml</relativePath> </parent>
<artifactId>auth-common</artifactId>
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> </dependency> </dependencies> </project>
|
子模块的 <relativePath> 指向 ../pom.xml,即父目录的 POM 文件。这样 Maven 可以正确找到父工程。
auth-core 模块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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>auth</artifactId> <version>1.0.0</version> <relativePath>../pom.xml</relativePath> </parent>
<artifactId>auth-core</artifactId>
<dependencies> <dependency> <groupId>com.example</groupId> <artifactId>auth-common</artifactId> <version>1.0.0</version> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency>
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> </dependency>
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <scope>runtime</scope> </dependency>
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <scope>runtime</scope> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
<dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <scope>provided</scope> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies> </project>
|
auth-web 模块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <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>auth</artifactId> <version>1.0.0</version> <relativePath>../pom.xml</relativePath> </parent>
<artifactId>auth-web</artifactId>
<dependencies> <dependency> <groupId>com.example</groupId> <artifactId>auth-core</artifactId> <version>1.0.0</version> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
|
第三章. 统一响应封装
3.1. 为什么需要统一响应
假设我们有两个接口,一个返回 Token 对象,一个返回 User 对象。前端收到的响应格式完全不同:
1 2 3 4 5 6 7 8 9 10 11
| { "accessToken": "token123", "refreshToken": "refresh456" }
{ "id": 1001, "username": "admin" }
|
前端开发者会抱怨:“为什么每个接口的格式都不一样?我怎么知道请求成功还是失败?”
我们需要定义一个统一的响应格式,所有接口都返回这个结构:
1 2 3 4 5 6 7
| { "code": 200, "message": "操作成功", "data": { } }
|
3.2. Result 泛型类设计
我们使用泛型 Result<T> 来封装响应,其中 T 可以是任何类型的数据。
📄 文件路径:auth-common/src/main/java/com/example/auth/common/model/Result.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| package com.example.auth.common.model;
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data @NoArgsConstructor @AllArgsConstructor public class Result<T> implements Serializable { private Integer code; private String message; private T data;
public static <T> Result<T> ok(T data) { return new Result<>(200, "操作成功", data); }
public static <T> Result<T> ok() { return new Result<>(200, "操作成功", null); }
public static <T> Result<T> fail(Integer code, String message) { return new Result<>(code, message, null); }
public static <T> Result<T> fail(String message) { return new Result<>(500, message, null); } }
|
现在,我们的接口可以统一返回格式:
1 2 3 4 5 6 7 8 9 10 11 12
| @PostMapping("/login") public Result<AuthToken> login() { AuthToken token = new AuthToken("token123", "refresh456"); return Result.ok(token); }
@GetMapping("/user") public Result<User> getUser() { User user = new User(1001L, "admin"); return Result.ok(user); }
|
前端收到的响应格式完全一致:
1 2 3 4 5 6 7 8
| { "code": 200, "message": "操作成功", "data": { "accessToken": "token123", "refreshToken": "refresh456" } }
|
3.3. 雪花算法工具类
在后续章节中,我们需要生成全局唯一的用户 ID。我们使用 Hutool 提供的雪花算法生成 19 位 Long 类型的趋势递增 ID来实现。
📄 文件路径:auth-common/src/main/java/com/example/auth/common/util/SnowflakeIdGenerator.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.example.auth.common.util;
import cn.hutool.core.lang.Snowflake; import cn.hutool.core.util.IdUtil;
public class SnowflakeIdGenerator {
private static final Snowflake SNOWFLAKE = IdUtil.getSnowflake(1, 1);
public static Long nextId() { return SNOWFLAKE.nextId(); } }
|
第四章. 本章小结
我们完成了标准 Maven 多模块架构的搭建,并封装了统一响应格式。
核心成果:
| 模块 | 职责 | 依赖关系 |
|---|
| auth (根 POM) | 聚合器 + 父工程 | 无 |
| auth-common | 公共基础模块 | 继承 auth |
| auth-core | 认证核心模块 | 继承 auth,依赖 auth-common |
| auth-web | Web 应用模块 | 继承 auth,依赖 auth-core |
架构优势:
- 职责分离:每个模块有明确的职责,易于维护
- 可复用性:auth-common 可以被其他项目复用
- 易于测试:auth-core 可以独立测试,不依赖 Web 层
- 版本统一:所有依赖版本在根 POM 中统一管理
- 结构清晰:采用标准的 Maven 多模块结构,减少了多余的目录层级
核心类速查:
| 类名 | 方法 | 作用 |
|---|
| Result | ok(T data) | 成功响应(带数据) |
| Result | ok() | 成功响应(不带数据) |
| Result | fail(Integer code, String message) | 失败响应 |
| SnowflakeIdGenerator | nextId() | 生成全局唯一 ID |