2026 最新 Spring Cloud 实战:从零搭建微服务全家桶(含完整代码)
本文基于 Spring Cloud 2025.0.3(Northfields)+ Spring Boot 3.5.16 + JDK 17 实测可运行,手把手带你落地注册中心、服务调用、熔断容错与统一网关四大核心组件。文末附 2025.1(Oakwood)+ Spring Boot 4.0 新版本前瞻。
一、引言:微服务为什么需要 Spring Cloud
当单体应用长到几万、十几万行,团队开始被"改一处、动全身"折磨:发布要全量、扩容要整体、一个模块雪崩拖垮全局。
微服务把系统按业务能力拆成一组独立部署的小服务,解决了 scale 与自治的问题,但也带来了新难题:
- 服务实例动态上下线,怎么互相发现?
- 几十个服务互相调用,怎么写更优雅?
- 某个下游挂了,怎么防止故障扩散(雪崩)?
- 前端要对接 N 个服务,怎么统一入口、统一鉴权?
这就是 Spring Cloud 的定位:它不是单一框架,而是一套经过生产验证的微服务解决方案集合,在 Spring Boot 的基础上,把 Netflix、Alibaba、Resilience4j 等生态的能力"开箱即用"地整合起来。你只需要加依赖、写注解、配 YAML,就能拥有工业级的微服务基础设施。
💡 小贴士:Spring Cloud 依托 Spring Boot 的自动配置(AutoConfiguration),让"约定优于配置"在微服务场景下成为现实——这也是它比自己手搓 RPC/注册中心省心得多的根本原因。
二、版本选型指南:先选对版本,少踩三年坑
Spring Cloud 采用 Release Train(发布列车) 命名,每个代号对应一个兼容的 Spring Boot 大版本区间。版本不兼容是新手踩得最惨的坑,务必先对齐。
2.1 版本地图(兼容性)
| Release Train | 代号 | 兼容 Spring Boot | 关键要求 | 状态 |
|---|---|---|---|---|
| Spring Cloud 2025.0.x | Northfields | Spring Boot 3.5.x | JDK 17 | ✅ 当前最主流稳定组合,最新服务版本 2025.0.3(2026-06 发布) |
| Spring Cloud 2025.1.x | Oakwood | Spring Boot 4.0.x | JDK 17+ | 🚀 最新前沿,2025-11 随 Boot 4.0 发布(基于 Spring Framework 7.0、Jakarta EE 10、原生镜像性能提升) |
| Spring Cloud 2022.0.x | Kilburn | Spring Boot 3.0/3.1 | JDK 17 | ⚠️ 已 EOL |
| Spring Cloud 2021.0.x | Jubilee | Spring Boot 2.6/2.7 | JDK 8+ | ⚠️ 已 EOL |
| Spring Cloud 2020.0.x | Ilford | Spring Boot 2.4/2.5 | JDK 8+ | ⚠️ 已 EOL |
| Hoxton / Greenwich | — | Spring Boot 2.2/2.1 | JDK 8 | ⚠️ 已 EOL |
⚠️ 注意:Hoxton / Greenwich / 2020.0 / 2021.0 / 2022.0 等旧 release train 均已 EOL(停止维护),生产环境不要再新建项目使用它们。新建项目请直接上 2025.0.x。
2.2 选型建议
- 🆕 新项目 / 生产环境:无脑选 Spring Cloud 2025.0.x + Spring Boot 3.5.x(本文演示组合),稳定、文档全、踩坑少。
- 🧪 尝鲜 / 预研:可上 Spring Cloud 2025.1.x(Oakwood)+ Spring Boot 4.0.x,体验原生镜像(Native Image)与 Gateway 5.0 异步能力。
- 🚫 强制底线:Spring Boot 4.0 是首个彻底告别 Java 8、强制要求 JDK 17+ 的版本。即便用 3.5,也建议直接 JDK 17,避免后续升级卡在 JDK 8 上。
💡 小贴士:Spring Cloud 版本号形如
2025.0.3,其中2025.0是 release train,3是补丁号。BOM(依赖管理清单)里通常只写2025.0.3即可,Maven 会据此锁定整套组件的兼容版本。
三、项目骨架:父工程 pom.xml
我们用多模块 Maven 工程组织:一个 parent 管版本,下面挂 eureka-server、user-service、order-service、gateway-service 四个模块。
父工程核心是 spring-boot-starter-parent 锁定 Boot 版本 + spring-cloud-dependencies BOM 锁定 Cloud 版本,二者对齐即可避免"依赖地狱"。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.16</version>
<relativePath/>
</parent>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2025.0.3</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
⚠️ 注意:
<scope>import</scope>的 BOM 只能出现在dependencyManagement里,它的作用是"把 Spring Cloud 全家桶的版本号一次性导入",子模块再引spring-cloud-starter-*时就不用写版本号了。
父工程的 <modules> 大致如下(供参考,非必须粘贴):
<modules>
<module>eureka-server</module>
<module>user-service</module>
<module>order-service</module>
<module>gateway-service</module>
</modules>
四、服务注册与发现:Eureka
微服务第一步——让服务能互相找到彼此。Eureka 是 Netflix 出品的注册中心:服务启动时把自己(IP、端口、服务名)注册进去,调用方从注册表拉取可用实例。
4.1 Eureka Server(注册中心)
模块 eureka-server 依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
启动类(加 @EnableEurekaServer):
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
application.yml(单机版,关闭自我注册与拉取,避免启动报错):
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
instance:
hostname: localhost
💡 小贴士:
8761是 Eureka 的默认端口。单机演示时务必把register-with-eureka和fetch-registry设为false,否则它会尝试连自己形成集群而报错。生产集群再把它们打开即可。
4.2 user-service(服务提供者)
模块 user-service 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
启动类(加 @EnableDiscoveryClient 声明为可发现的服务):
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
Controller(提供一个最朴素的 REST 接口,返回 JSON):
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public Map<String, Object> getUser(@PathVariable Long id) {
Map<String, Object> user = new HashMap<>();
user.put("id", id);
user.put("name", "张三");
user.put("email", "zhangsan@example.com");
return user;
}
}
⚠️ 注意:Spring Boot 3.x 全面拥抱 Jakarta EE,包名已从
javax.servlet.*变为jakarta.servlet.*。如果你从老项目拷贝代码出现javax找不到,多半是依赖或 JDK/Jakarta 没对齐——Boot 3.5 + Cloud 2025.0 必须用 Jakarta 体系,不要混用javax。
application.yml(指定服务名并指向注册中心):
server:
port: 8081
spring:
application:
name: user-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
启动后访问 http://localhost:8761 就能在控制台的 Instances currently registered with Eureka 列表里看到 USER-SERVICE。
五、服务间调用:Spring Cloud OpenFeign
order-service 需要查用户。传统做法是自己拼 RestTemplate + URL,既丑又难维护。OpenFeign 用"声明式接口"把远程调用写成本地方法调用,Spring Cloud 在运行期自动生成实现、自动做负载均衡(Ribbon/Clientside LB)。
模块 order-service 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
启动类(加 @EnableFeignClients 开启 Feign 扫描):
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
Feign 接口(声明"我要调哪个服务、哪个 URL",并指定熔断降级类):
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient {
@GetMapping("/users/{id}")
Map<String, Object> getUser(@PathVariable("id") Long id);
}
@Component
class UserClientFallback implements UserClient {
@Override
public Map<String, Object> getUser(Long id) {
Map<String, Object> fallback = new HashMap<>();
fallback.put("id", id);
fallback.put("name", "未知(服务降级)");
return fallback;
}
}
Controller(像调本地方法一样调远程服务):
@RestController
@RequestMapping("/orders")
public class OrderController {
private final UserClient userClient;
public OrderController(UserClient userClient) {
this.userClient = userClient;
}
@GetMapping("/{id}/user")
public Map<String, Object> orderUser(@PathVariable Long id) {
Map<String, Object> order = new HashMap<>();
order.put("orderId", id);
order.put("user", userClient.getUser(id));
return order;
}
}
💡 小贴士:
@FeignClient(name = "user-service")里的name就是注册中心里的服务名(大小写不敏感)。Feign 会结合 Eureka 自动把user-service解析成具体host:port,并内置客户端负载均衡。
六、熔断与容错:Resilience4j
没有熔断的微服务等于"把命运交给下游"。假设 user-service 宕机,order-service 的每次调用都会线程堆积 → 超时 → 资源耗尽 → 自己也挂,雪崩由此而来。
Resilience4j 提供熔断器(CircuitBreaker):当失败率超过阈值,直接"跳闸"走降级逻辑,不再盲目重试下游。spring-cloud-starter-circuitbreaker-resilience4j 已在上一步引入,只需开启 + 配置。
application.yml(开启 Feign 熔断,并配置 Resilience4j 实例):
server:
port: 8082
spring:
application:
name: order-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
# 开启 Feign 与 Resilience4j 熔断
feign:
circuitbreaker:
enabled: true
resilience4j:
circuitbreaker:
instances:
user-service:
failure-rate-threshold: 50
minimum-number-of-calls: 5
wait-duration-in-open-state: 5s
配置含义速记:
| 配置项 | 含义 |
|---|---|
failure-rate-threshold: 50 | 失败率达到 50% 即熔断 |
minimum-number-of-calls: 5 | 至少统计 5 次调用才计算失败率(防止冷启动误判) |
wait-duration-in-open-state: 5s | 熔断后 5 秒进入半开,试探下游是否恢复 |
⚠️ 注意:降级类
UserClientFallback必须被 Spring 扫描到(加了@Component),否则熔断触发时会找不到降级实现而抛No fallback异常。降级不是"可有可无的兜底",而是系统韧性的最后一道防线。
七、统一网关:Spring Cloud Gateway(⚠️ 2025.0 已改名)
所有外部请求不该直连各微服务,而该先过网关:统一入口、路由转发、鉴权、限流、跨域。Spring Cloud Gateway 是官方推荐的新一代网关(基于 WebFlux 响应式)。
7.1 ⚠️ 2025.0 破坏性变更:模块名 & 配置前缀都改了!
这是本文最关键的踩坑点,务必记牢:
- ❌ 旧模块名
spring-cloud-starter-gateway已废弃。 - ✅ 新模块名改用
spring-cloud-starter-gateway-server-webflux(WebFlux 响应式风格)或spring-cloud-starter-gateway-server-webmvc(传统 WebMvc 风格)。 - ❌ 旧配置前缀
spring.cloud.gateway.*已变更。 - ✅ 新前缀改为
spring.cloud.gateway.server.webflux.*(或对应webmvc)。
⚠️ 注意(重点):如果你照搬网上的老教程,用
spring-cloud-starter-gateway+spring.cloud.gateway.routes写法,在 Spring Cloud 2025.0 下会依赖下载不到或路由完全不生效。本文全部使用 2025.0 的新写法。
7.2 gateway-service 依赖(新模块名)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway-server-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
启动类(网关本身也是 Eureka 客户端,用于服务发现路由):
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayServiceApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayServiceApplication.class, args);
}
}
7.3 application.yml(新前缀 + 路由配置)
server:
port: 8080
spring:
application:
name: gateway-service
cloud:
gateway:
server:
webflux:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
配置说明:
discovery.locator.enabled: true:开启基于服务发现的路由,网关能直接按服务名转发。lb://user-service:lb表示客户端负载均衡,后面跟注册中心里的服务名。Path=/api/users/**+StripPrefix=1:外部访问/api/users/1,网关剥掉第一层/api,实际转发到user-service的/users/1。
💡 小贴士:用
StripPrefix=1是常见套路——对外暴露统一/api前缀,对内保持各服务的原生路径,前后端约定清晰、网关也能做统一鉴权。
八、运行与验证
8.1 启动顺序
微服务有依赖关系,严格按以下顺序启动(等上一个完全起来再起下一个):
1. eureka-server (8761) ← 先起注册中心
2. user-service (8081) ← 服务提供者
3. order-service (8082) ← 服务消费者
4. gateway-service (8080) ← 最后起网关
8.2 验证 URL
| 验证项 | 地址 | 预期 |
|---|---|---|
| Eureka 控制台 | http://localhost:8761 | 看到 USER-SERVICE / ORDER-SERVICE / GATEWAY-SERVICE 已注册 |
| 直连 user | http://localhost:8081/users/1 | 返回 {"id":1,"name":"张三","email":"zhangsan@example.com"} |
| 直连 order | http://localhost:8082/orders/1/user | 返回订单 + 嵌套用户信息 |
| 经网关访问 user | http://localhost:8080/api/users/1 | 经网关转发,结果同上 |
| 经网关访问 order | http://localhost:8080/api/orders/1/user | 经网关转发,结果同上 |
💡 小贴士:先验证直连(8081/8082)通,再验证网关(8080)。如果网关返回 404,优先检查
StripPrefix与Path是否匹配、服务名大小写、以及服务是否真的注册进了 Eureka。
8.3 熔断自测
手动停掉 user-service,再次访问 http://localhost:8082/orders/1/user(或经网关 http://localhost:8080/api/orders/1/user),应返回降级结果:
{ "orderId": 1, "user": { "id": 1, "name": "未知(服务降级)" } }
说明熔断生效,系统未被下游拖垮。
九、新版本前瞻:2025.1(Oakwood)+ Spring Boot 4.0
如果你准备面向未来,可以关注 Spring Cloud 2025.1.x(Oakwood) 与 Spring Boot 4.0.x 的组合(2025-11 发布)。关键升级:
- 🚀 Spring Boot 4.0 是首个彻底告别 Java 8、强制要求 JDK 17+ 的版本——老项目升级前务必确认 JDK 基线。
- 🧱 基于 Spring Framework 7.0 与 Jakarta EE 10,响应式与 AOT(Ahead-Of-Time)编译更成熟。
- ⚡ 原生镜像(Native Image)性能大幅提升:GraalVM 编译下启动秒级、内存占用显著下降,非常适合云原生 Serverless 场景。
- 🌐 Spring Cloud Gateway 5.0:强化异步处理与 WebSocket 支持,长连接 / 双向通信场景更稳。
- 🔧 配置与 API 进一步收敛到 Jakarta 体系,旧
javax代码需全面迁移。
💡 选型一句话:当下求稳用 2025.0 + Boot 3.5;想追原生镜像与 Gateway 5.0 异步能力,预研 2025.1 + Boot 4.0。两者都要求 JDK 17,迁移第一步永远是先升 JDK。
十、总结 & 学习资源
本文我们从零搭起了一套可运行的 Spring Cloud 微服务全家桶:
- Eureka 解决"服务在哪"——注册与发现;
- OpenFeign 解决"怎么优雅调用"——声明式客户端;
- Resilience4j 解决"下游挂了怎么办"——熔断降级保韧性;
- Spring Cloud Gateway 解决"统一入口"——路由转发(注意 2025.0 的模块改名与配置前缀变化)。
记住三个不踩坑要点:版本对齐(2025.0 ↔ Boot 3.5)、JDK 17 起步、Gateway 用新模块名 spring-cloud-starter-gateway-server-webflux 与新前缀 spring.cloud.gateway.server.webflux.*。
推荐学习资源
- 📘 Spring 官网:https://spring.io
- 📗 Spring Cloud 官方文档:https://docs.spring.io/spring-cloud/docs/current/reference/html/
- 📙 Spring Boot 官方文档:https://docs.spring.io/spring-boot/docs/current/reference/html/
- 🛠 Spring Initializr(快速生成骨架):https://start.spring.io
如果本文对你有帮助,欢迎 点赞 / 收藏 / 关注,后续我会更新 Spring Cloud 2025.1 + Boot 4.0 原生镜像实战 与 Gateway 鉴权限流篇。评论区聊聊你正在用的 Spring Cloud 版本吧 👇
✍️ 本文示例代码均基于 Spring Cloud 2025.0.3 + Spring Boot 3.5.16 + JDK 17 实测,可直接复制到 优快云 / 你的工程中使用。
703



被折叠的 条评论
为什么被折叠?



