Spring Cloud 学习与实践(1):项目骨架搭建
文章目录
第 1 章:Spring Cloud Alibaba 项目骨架搭建
本章目标:创建 Spring Cloud Alibaba 微服务学习项目的 Maven 多模块骨架,统一依赖版本,建立公共模块,为后续用户、商品、订单、网关、认证等服务开发打基础。
1. 本章目标
本章主要完成以下内容:
1. 创建 cloud-demo Maven 父工程
2. 创建 cloud-common、cloud-api、cloud-gateway、cloud-auth、cloud-user、cloud-product、cloud-order 七个子模块
3. 在父工程中统一管理 JDK、Spring Boot、Spring Cloud、Spring Cloud Alibaba、MyBatis-Plus 等版本
4. 在 cloud-common 中建立统一返回、错误码、业务异常、全局异常处理等公共基础能力
5. 执行 mvn clean package,确认整个多模块项目可以正常编译
本章暂时不开发用户、商品、订单业务,也暂时不接入 Nacos、MySQL、Gateway、Feign 等组件。
2. 为什么要先搭建父工程
微服务项目一般会拆分成多个独立服务,例如:
用户服务
商品服务
订单服务
认证服务
网关服务
如果每个服务都单独管理自己的依赖版本,后续很容易出现版本不一致、依赖冲突、运行时报错等问题。
因此,正式开发业务之前,需要先通过 Maven 父工程统一管理:
项目模块
依赖版本
插件版本
公共配置
这样可以保证后续每个服务都在同一套版本体系下开发,减少兼容性问题。
一句话总结:
父工程负责统一版本,公共模块负责统一规范。
3. 项目结构
本章创建的项目结构如下:
cloud-demo
├── pom.xml
├── cloud-common
│ ├── pom.xml
│ └── src/main/java/com/example/cloud/common
├── cloud-api
│ └── pom.xml
├── cloud-gateway
│ └── pom.xml
├── cloud-auth
│ └── pom.xml
├── cloud-user
│ └── pom.xml
├── cloud-product
│ └── pom.xml
└── cloud-order
└── pom.xml
各模块职责如下:
| 模块 | 作用 |
|---|---|
| cloud-common | 公共返回、错误码、异常处理、工具类 |
| cloud-api | 后续存放 Feign 接口、DTO、服务间调用契约 |
| cloud-gateway | 后续作为统一网关 |
| cloud-auth | 后续实现登录认证和 JWT 签发 |
| cloud-user | 用户服务 |
| cloud-product | 商品服务 |
| cloud-order | 订单服务 |
4. 技术版本
本项目统一使用以下版本:
JDK:17
Spring Boot:2.7.18
Spring Cloud:2021.0.8
Spring Cloud Alibaba:2021.0.5.0
MyBatis-Plus:3.5.7
MySQL Driver:8.0.33
Lombok:1.18.30
JWT:0.11.5
其中:
Spring Boot 负责单个服务的快速开发与启动
Spring Cloud 负责 Gateway、OpenFeign、LoadBalancer 等官方微服务组件
Spring Cloud Alibaba 负责 Nacos、Sentinel 等 Alibaba 生态组件
IDEA截图:




依然手动创建子模块,并手写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.cloud</groupId>
<artifactId>cloud-demo</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>换成模块名</artifactId>
<packaging>jar</packaging>
</project>
5. 父工程 pom.xml 关键配置
父工程 cloud-demo/pom.xml 的核心作用是:
1. 声明当前项目是 Maven 父工程
2. 管理所有子模块
3. 统一管理依赖版本
4. 统一管理插件版本
5.1 packaging 必须是 pom
<packaging>pom</packaging>
如果不写,Maven 默认会认为当前工程是 jar 项目。父工程本身不打业务 jar,它的作用是管理子模块,所以必须声明为 pom。
5.2 modules 管理子模块
<modules>
<module>cloud-common</module>
<module>cloud-api</module>
<module>cloud-gateway</module>
<module>cloud-auth</module>
<module>cloud-user</module>
<module>cloud-product</module>
<module>cloud-order</module>
</modules>
执行:
mvn clean package
时,Maven 会按照模块依赖关系依次构建这些子模块。
5.3 dependencyManagement 统一依赖版本
父工程中通过 dependencyManagement 管理依赖版本,例如:
<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.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
注意:
dependencyManagement 只负责版本约束,不会真正引入依赖。
dependencies 才会真正引入依赖。
例如父工程管理了 MyBatis-Plus 的版本,不代表所有模块都自动引入了 MyBatis-Plus。只有子模块自己写了依赖,才会真正引入。
父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>
<groupId>com.example.cloud</groupId>
<artifactId>cloud-demo</artifactId>
<version>1.0.0</version>
<!-- 父工程必须声明,否则默认为jar-->
<packaging>pom</packaging>
<name>cloud-demo</name>
<description>Spring Cloud Alibaba microservice demo</description>
<modules>
<module>cloud-common</module>
<module>cloud-api</module>
<module>cloud-gateway</module>
<module>cloud-auth</module>
<module>cloud-user</module>
<module>cloud-product</module>
<module>cloud-order</module>
</modules>
<properties>
<!-- 给 Spring Boot 生态看的统一 Java 版本属性-->
<java.version>17</java.version>
<!-- 告诉编译器源码按 Java 17 语法处理-->
<maven.compiler.source>17</maven.compiler.source>
<!-- 告诉编译器生成 Java 17 字节码-->
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>2.7.18</spring-boot.version>
<spring-cloud.version>2021.0.8</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
<mybatis-plus.version>3.5.7</mybatis-plus.version>
<mysql.version>8.0.33</mysql.version>
<lombok.version>1.18.30</lombok.version>
<jjwt.version>0.11.5</jjwt.version>
</properties>
<!-- 只管理依赖版本-->
<dependencyManagement>
<dependencies>
<!-- Spring Boot 版本管理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud 版本管理 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud Alibaba 版本管理 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- JWT,后续 cloud-auth 使用 -->
<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>
</dependencies>
</dependencyManagement>
<build>
<!-- 管理插件版本和默认配置-->
<pluginManagement>
<plugins>
<!-- 编译插件,统一 JDK17 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- Spring Boot 打包插件,业务模块后续使用 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
6. cloud-common 公共模块
cloud-common 是公共模块,用于存放所有服务都可能用到的基础能力。
本章创建了以下公共类:
Result<T>:统一返回结果
ErrorCode:统一错误码
BizException:业务异常
GlobalExceptionHandler:全局异常处理器
6.1 cloud-common 的 pom.xml
cloud-common 当前引入:
<dependencies>
<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>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
原因:
spring-boot-starter-web:提供 @RestControllerAdvice、@ExceptionHandler 等 Web 异常处理能力
spring-boot-starter-validation:后续 DTO 参数校验会用到
lombok:减少 getter、setter、构造方法等样板代码
注意:cloud-common 是普通 jar 模块,不是启动服务,因此不需要启动类,也不需要 application.yml。
完整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>com.example.cloud</groupId>
<artifactId>cloud-demo</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>cloud-common</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Web 依赖:为了使用 @RestControllerAdvice、@ExceptionHandler -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 参数校验:后续 DTO 参数校验使用 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Lombok:减少 getter/setter 等样板代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
7. 统一返回 Result
统一返回的目标是让所有接口都保持相同结构:
{
"code": 0,
"message": "success",
"data": {}
}
字段含义:
| 字段 | 说明 |
|---|---|
| code | 业务状态码 |
| message | 提示信息 |
| data | 返回数据 |
常用方法:
Result.success()
Result.success(data)
Result.fail(code, message)
Result.fail(errorCode)
统一返回的好处:
前端处理更统一
接口风格更规范
异常返回更清晰
后续网关、Feign、日志排查更方便
代码:
package com.example.cloud.common.result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success() {
return new Result<>(0, "success", null);
}
public static <T> Result<T> success(T data) {
return new Result<>(0, "success", data);
}
public static <T> Result<T> fail(Integer code, String message) {
return new Result<>(code, message, null);
}
public static <T> Result<T> fail(ErrorCode errorCode) {
return new Result<>(errorCode.getCode(), errorCode.getMessage(), null);
}
public static <T> Result<T> fail(ErrorCode errorCode, String message) {
return new Result<>(errorCode.getCode(), message, null);
}
}
8. 统一错误码 ErrorCode
错误码用于规范系统中的常见错误:
SUCCESS:成功
PARAM_ERROR:请求参数错误
UNAUTHORIZED:未登录或登录已过期
FORBIDDEN:没有权限访问
NOT_FOUND:资源不存在
BIZ_ERROR:业务处理失败
SYSTEM_ERROR:系统异常
REMOTE_CALL_ERROR:远程服务调用失败
后续项目中不要随便返回字符串错误,而是尽量通过统一错误码表达错误类型。
完整代码:
package com.example.cloud.common.result;
import lombok.Getter;
@Getter
public enum ErrorCode {
SUCCESS(0, "success"),
PARAM_ERROR(40000, "请求参数错误"),
UNAUTHORIZED(40100, "未登录或登录已过期"),
FORBIDDEN(40300, "没有权限访问"),
NOT_FOUND(40400, "资源不存在"),
BIZ_ERROR(50000, "业务处理失败"),
SYSTEM_ERROR(50001, "系统异常"),
REMOTE_CALL_ERROR(50002, "远程服务调用失败");
private final Integer code;
private final String message;
ErrorCode(Integer code, String message) {
this.code = code;
this.message = message;
}
}
9. 业务异常 BizException
业务异常用于主动抛出可预期的业务错误。
例如后续商品库存不足时,可以抛出:
throw new BizException(ErrorCode.BIZ_ERROR, "商品库存不足");
它和系统异常的区别是:
业务异常:用户操作或业务规则导致的可预期错误
系统异常:代码 bug、依赖故障、数据库异常等不可预期错误
完整代码:
package com.example.cloud.common.exception;
import com.example.cloud.common.result.ErrorCode;
import lombok.Getter;
@Getter
public class BizException extends RuntimeException {
private final Integer code;
public BizException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.code = errorCode.getCode();
}
public BizException(ErrorCode errorCode, String message) {
super(message);
this.code = errorCode.getCode();
}
public BizException(Integer code, String message) {
super(message);
this.code = code;
}
}
10. 全局异常处理 GlobalExceptionHandler
全局异常处理器的作用是统一捕获异常,并转换成统一返回格式。
主要处理:
BizException
MethodArgumentNotValidException
BindException
Exception
处理逻辑:
业务异常:返回业务错误码和错误信息
参数校验异常:返回参数错误
未知异常:记录 error 日志,返回系统异常
这样 Controller 中不需要到处写 try-catch,代码更干净。
完整代码:
package com.example.cloud.common.exception;
import com.example.cloud.common.result.ErrorCode;
import com.example.cloud.common.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BizException.class)
public Result<Void> handleBizException(BizException e) {
log.warn("业务异常:{}", e.getMessage());
return Result.fail(e.getCode(), e.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldErrors().isEmpty()
? ErrorCode.PARAM_ERROR.getMessage()
: e.getBindingResult().getFieldErrors().get(0).getDefaultMessage();
log.warn("参数校验异常:{}", message);
return Result.fail(ErrorCode.PARAM_ERROR, message);
}
@ExceptionHandler(BindException.class)
public Result<Void> handleBindException(BindException e) {
String message = e.getBindingResult().getFieldErrors().isEmpty()
? ErrorCode.PARAM_ERROR.getMessage()
: e.getBindingResult().getFieldErrors().get(0).getDefaultMessage();
log.warn("参数绑定异常:{}", message);
return Result.fail(ErrorCode.PARAM_ERROR, message);
}
@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception e) {
log.error("系统异常", e);
return Result.fail(ErrorCode.SYSTEM_ERROR);
}
}
11. 编译验证
在项目根目录执行:
mvn clean package

本次构建结果:
cloud-demo ......................................... SUCCESS
cloud-common ....................................... SUCCESS
cloud-api .......................................... SUCCESS
cloud-gateway ...................................... SUCCESS
cloud-auth ......................................... SUCCESS
cloud-user ......................................... SUCCESS
cloud-product ...................................... SUCCESS
cloud-order ........................................ SUCCESS
BUILD SUCCESS
这说明:
父工程结构正确
子模块识别正常
cloud-common 中的 4 个 Java 类编译成功
Maven 多模块构建成功
构建过程中部分模块出现:
JAR will be empty - no content was marked for inclusion!
这是正常现象。因为 cloud-api、cloud-gateway、cloud-auth、cloud-user、cloud-product、cloud-order 当前只创建了 pom.xml,还没有 Java 代码,所以 Maven 提示 jar 为空。后续章节逐步添加代码后,这个警告会自然消失。
12. 本章常见问题
12.1 dependencyManagement 和 dependencies 有什么区别?
dependencyManagement 只管理版本,不会真正引入依赖。
dependencies 才会真正引入依赖。
父工程中通常使用 dependencyManagement 统一版本,子模块按需使用 dependencies 引入具体依赖。
12.2 为什么 Spring Cloud 和 Spring Cloud Alibaba 要同时引入?
因为它们负责的组件不同。
Spring Cloud:Gateway、OpenFeign、LoadBalancer 等官方组件
Spring Cloud Alibaba:Nacos、Sentinel 等 Alibaba 生态组件
所以父工程中需要同时导入两个 BOM,后续子模块再按需引入具体 starter。
12.3 空 jar 警告是不是错误?
不是。
当前部分模块还没有 Java 代码,所以 Maven 打包时提示 jar 为空。这不影响构建成功,也不影响后续开发。
12.4 cloud-common 为什么需要 spring-boot-starter-web?
因为 GlobalExceptionHandler 使用了:
@RestControllerAdvice
@ExceptionHandler
这些注解属于 Spring Web 异常处理体系,所以 cloud-common 需要引入 Web 相关依赖。
13. 本章结论
本章完成了 Spring Cloud Alibaba 微服务项目的基础骨架搭建。
当前项目已经具备:
Maven 多模块结构
统一版本管理
公共模块基础能力
全局异常处理基础
可正常编译的项目骨架
下一章将进入:
第 2 章:用户服务 cloud-user 开发
下一章会开始接入:
Spring Boot Web
MySQL
MyBatis-Plus
用户表
用户 CRUD 接口
统一返回
更多推荐

所有评论(0)