Spring Boot 微服务实战指南
CSDN 上 Java 相关内容常年占据技术社区流量榜首,而 Spring Boot 是 Java 后端的事实标准。本文从零开始,带你把一个 Spring Boot 单体项目演进为微服务架构,覆盖核心原理、常用组件、微服务拆分、注册中心、配置中心、网关、熔断等完整链路。
一、Spring Boot 快速上手
1.1 为什么是 Spring Boot
过去用 Spring 开发,要手动配置 XML、配置数据源、配置事务、配置 MVC------每个项目几百行配置。Spring Boot 的核心哲学是约定大于配置:
- 自动配置:根据依赖自动装配 Bean;
- 内嵌服务器:Tomcat/Jetty 打包进 jar,不用单独装;
- Starter 机制:一个依赖引入一组能力。
对比传统 Spring:
| 维度 | 传统 Spring | Spring Boot |
|---|---|---|
| 配置 | XML/注解,量大 | 自动配置,少量 application.yml |
| 部署 | 打 war 扔 Tomcat | 打 jar 直接 java -jar |
| 依赖 | 手动管理版本 | Starter + 版本管理 |
| 监控 | 无 | Actuator 自带 |
1.2 创建项目
推荐用 Spring Initializr(start.spring.io)或 IDEA 内置:
bash
# 或命令行
curl https://start.spring.io/starter.zip \
-d dependencies=web,data-jpa,mysql,validation \
-d groupId=com.example -d artifactId=demo -o demo.zip
核心依赖:
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
1.3 第一个接口
java
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public Result<User> getUser(@PathVariable Long id) {
return Result.ok(userService.getById(id));
}
}
注意 :Spring Boot 3.x 用 jakarta.* 包名,不再是 javax.*------很多老教程会踩这个坑。
1.4 配置体系
yaml
spring:
application:
name: user-service
datasource:
url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf8mb4
username: root
password: 123456
server:
port: 8081
配置优先级:命令行参数 > application-{profile}.yml > application.yml > 默认值。
多环境配置:
yaml
# application-dev.yml
# application-prod.yml
# 启动时指定
java -jar app.jar --spring.profiles.active=prod
二、核心原理:自动配置
2.1 自动配置是怎么工作的
@SpringBootApplication 是三个注解的合成:
java
@SpringBootConfiguration // 标记为配置类
@EnableAutoConfiguration // 开启自动配置
@ComponentScan // 扫描当前包及子包
@EnableAutoConfiguration 会读取 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,把里面的自动配置类全部加载,但条件化生效:
java
@Configuration
@ConditionalOnClass(DataSource.class) // 类在 classpath 才生效
@ConditionalOnMissingBean(DataSource.class) // 用户没自定义才生效
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration { ... }
核心:条件注解决定"什么时候生效"。这也是为什么引入了 spring-boot-starter-data-jpa 就自动配好了数据源。
2.2 常用条件注解
@ConditionalOnClass:classpath 有某个类;@ConditionalOnMissingBean:容器里没有某个 Bean;@ConditionalOnProperty:配置项存在/等于某值;@ConditionalOnExpression:SpEL 表达式。
2.3 自定义 Starter
公司内部公共组件(如统一日志、统一异常处理)可以打成 Starter:
my-common-starter/
├── pom.xml
└── src/main/java/com/example/common/
├── MyAutoConfiguration.java
└── META-INF/spring/
└── org.springframework.boot.autoconfigure.AutoConfiguration.imports
java
@AutoConfiguration
@ConditionalOnClass(LogUtil.class)
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public LogFilter logFilter() {
return new LogFilter();
}
}
三、Spring Boot 实战要点
3.1 统一异常处理
生产环境不能让用户看到堆栈。用 @RestControllerAdvice:
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public Result<?> handleBusiness(BusinessException e) {
return Result.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<?> handleValid(MethodArgumentNotValidException e) {
String msg = e.getBindingResult().getFieldErrors()
.stream().map(FieldError::getDefaultMessage)
.collect(Collectors.joining(";"));
return Result.error(400, msg);
}
@ExceptionHandler(Exception.class)
public Result<?> handleUnknown(Exception e) {
log.error("系统异常", e);
return Result.error(500, "系统繁忙");
}
}
原则:已知业务异常返回业务码,未知异常只记日志不暴露堆栈。
3.2 参数校验
java
public class CreateUserRequest {
@NotBlank(message = "用户名不能为空")
private String username;
@Email(message = "邮箱格式错误")
private String email;
@Min(value = 1, message = "年龄最小为1")
private Integer age;
}
Controller 里加 @Valid:
java
@PostMapping
public Result<User> create(@Valid @RequestBody CreateUserRequest req) { ... }
3.3 日志
xml
<!-- logback-spring.xml -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
规范:
- 用
{}占位,不要字符串拼接; - 异常要打完整堆栈
log.error("xxx", e); - 日志分级:DEBUG 调试、INFO 业务、WARN 可恢复问题、ERROR 异常。
3.4 异步任务
java
@EnableAsync
@Configuration
public class AsyncConfig { ... }
@Service
public class NotifyService {
@Async
public void sendSms(String phone) {
// 异步发短信,不阻塞主流程
}
}
坑 :@Async 失效的常见原因------同一个类内自调用(this 调用不经过代理);用 Spring 容器注入的 bean 调才会走代理。
四、为什么需要微服务
4.1 单体应用的问题
单体项目(一个 war 包包含所有模块)发展到一定规模会遇到:
- 构建慢:几百万行代码,编译 10 分钟;
- 发布难:改一行也要全量发布,风险大;
- 团队冲突:100 人改同一个仓库,合并冲突频繁;
- 扩展不均衡:订单模块压力大要加机器,但用户模块也要一起扩;
- 技术绑定:想给报表模块用 Python 重写?不可能,都在一个包里。
4.2 微服务的思路
按业务边界拆成独立服务:
mall/
├── user-service 用户服务(端口 8081)
├── order-service 订单服务(8082)
├── product-service 商品服务(8083)
├── payment-service 支付服务(8084)
└── gateway-service 网关(8080)
每个服务:
- 独立仓库、独立部署;
- 独立数据库(数据隔离);
- 独立扩容;
- 通过 HTTP/RPC 通信。
4.3 微服务的代价
不要神化微服务------它的代价是实打实的:
- 运维复杂:20 个服务要监控 20 个进程;
- 链路排查难:一次请求跨 5 个服务,出问题要全链路查;
- 分布式事务:跨服务下单+扣库存,本地事务解决不了;
- 网络开销:本地方法调用变远程调用,慢 100 倍。
判断标准:团队 < 50 人、模块 < 10 个,先用单体+模块化,别硬上微服务。
五、微服务核心组件
5.1 注册中心:Nacos
服务要互相发现,注册中心是微服务的地基。国内主流是 Nacos(阿里开源,支持注册+配置二合一)。
yaml
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
服务启动后自动注册,其他服务通过服务名调用:
java
// 订单服务调用用户服务
@FeignClient("user-service")
public interface UserClient {
@GetMapping("/api/users/{id}")
Result<User> getUser(@PathVariable Long id);
}
Feign 声明式调用:像调本地方法一样调远程服务,底层是 HTTP。
5.2 配置中心:Nacos Config
配置集中管理,改配置不用重启:
yaml
spring:
config:
import: optional:nacos:user-service.yml
java
@RefreshScope
@Component
@ConfigurationProperties(prefix = "mall")
public class MallConfig {
private String welcomeMsg;
// getter/setter
}
改了 Nacos 里的配置,@RefreshScope 的 Bean 自动刷新。
5.3 网关:Spring Cloud Gateway
所有请求先进网关,做统一入口:
- 路由转发;
- 鉴权;
- 限流;
- 日志。
yaml
spring:
cloud:
gateway:
routes:
- id: user-route
uri: lb://user-service
predicates:
- Path=/api/users/**
- id: order-route
uri: lb://order-service
predicates:
- Path=/api/orders/**
网关里做统一鉴权:
java
@Component
public class AuthFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (token == null || !JwtUtil.verify(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
}
5.4 熔断降级:Sentinel
防止"一个服务挂了拖垮整个系统"------下单调库存,库存超时,不能一直等。
java
@SentinelResource(value = "createOrder", fallback = "orderFallback")
public Order createOrder(OrderDTO dto) {
return stockClient.deduct(dto); // 可能超时
}
public Order orderFallback(OrderDTO dto, Throwable e) {
log.error("下单降级: {}", e.getMessage());
return Order.degraded(); // 返回降级结果
}
Sentinel 核心概念:
- 熔断:错误率超阈值,直接拒绝一段时间;
- 降级:熔断后走 fallback 逻辑;
- 限流:QPS 超阈值丢弃请求。
5.5 链路追踪:SkyWalking
一次请求跨 5 个服务,出问题怎么定位?链路追踪记录每个环节耗时:
GET /api/orders/123
├── gateway: 5ms
├── order-service: 40ms
│ ├── DB 查询: 15ms
│ └── user-service 调用: 20ms
├── user-service: 18ms
SkyWalking 通过 Agent 无侵入接入,不用改代码。
六、实战:单体拆微服务
6.1 拆分步骤
- 找边界:按业务能力拆(用户/订单/商品/支付),不是按技术层拆(controller/service/dao 各拆一个服务是错的);
- 抽公共模块:统一返回、统一异常、工具类打成 common 包;
- 数据库拆分:每个服务独立库,用 Feign 调用代替跨库 JOIN;
- 改造调用:Service 间调用改成 HTTP;
- 加网关和鉴权;
- 加熔断、链路追踪、监控。
6.2 数据一致性
跨服务操作(下单:订单服务+扣库存+减积分)怎么保证?
- 本地事务 + 重试:先写订单,异步通知库存,失败重试;
- 事务消息(RocketMQ):本地事务提交后发消息;
- Seata:分布式事务框架,但性能损耗大,慎用。
经验:90% 的场景用"本地事务 + 消息 + 最终一致"就够,别一上来就 Seata。
6.3 一次完整下单链路
用户请求 POST /api/orders (网关)
→ gateway 鉴权
→ order-service 创建订单(本地事务)
→ 发 MQ 消息"订单已创建"
→ stock-service 消费消息,扣库存(幂等)
→ payment-service 处理支付
→ 用户轮询/推送查状态
七、Spring Boot 3 新特性
- Jakarta EE 9+:javax 改 jakarta;
- GraalVM 原生镜像:启动毫秒级、内存低,但反射受限;
- 虚拟线程(Java 21):百万级并发线程;
- Observability:Micrometer + OpenTelemetry 内置支持。
迁移注意 :Spring Boot 2 → 3 不是改版本号那么简单------javax.* 全部改 jakarta.*、spring.factories 改 imports 文件、一些 API 废弃。
八、面试高频题速览
- Spring Boot 自动配置原理 :
@EnableAutoConfiguration+ 条件注解 + imports 文件; - @Autowired 和 @Resource 区别:前者按类型,后者按名称;
- Bean 生命周期:实例化 → 属性填充 → Aware → BeanPostProcessor → init → 使用 → destroy;
- 循环依赖怎么解决:三级缓存(Spring 默认单例支持,构造器注入除外);
- 微服务拆分原则:高内聚低耦合、按业务边界、数据独立;
- 分布式事务方案:2PC/3PC、TCC、本地消息表、事务消息、Seata;
- 服务雪崩怎么防:超时、熔断(Sentinel/Hystrix)、限流、隔离、降级。
本章小结
Spring Boot 降低的是"写代码"的成本,微服务解决的是"扩展与协作"的问题,但两者都带来新的复杂度。工程上务实的路径是:单体起步 → 模块化 → 按需拆服务 → 配套治理组件。
下一篇讲 Redis------缓存、分布式锁、持久化,后端面试和实战的双热点。
(全文完)