<?xml version="1.0" encoding="utf-8"?>
<ul>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。本附录汇总常用配置、注解、命令和排查路径。Spring Boot 3.x 与 Spring Cloud 2023.x/2024.x 为主线，具体配置项随版本变化，使用前以当前版本文档为准。1. 常用依赖&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-starter-validation&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;    &lt;artifactId&gt;spring-cloud-starter-openfeign&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;    &lt;artifactId&gt;spring-cloud-starter-gateway-server-webflux&lt;/artifactId&gt;&lt;/dependency&gt;2. 常用注解            注解      用途                  @SpringBootApplication      启动类              @Configuration      配置类              @Bean      Bean 定义              @Component      组件扫描              @Service      服务              @Repository      数据访问              @Autowired      注入              @Qualifier      指定候选              @Primary      优先候选              @Value      注入配置              @ConfigurationProperties      配置绑定              @Transactional      事务              @Cacheable      缓存              @Scheduled      调度              @Async      异步              @Valid      参数校验              @RestControllerAdvice      全局异常              @FeignClient      HTTP 客户端              @LoadBalanced      客户端负载均衡              @RefreshScope      刷新作用域      3. 配置文件模板spring:  application:    name: order-service  profiles:    active: ${SPRING_PROFILES_ACTIVE:dev}  datasource:    url: jdbc:mysql://${DB_HOST:localhost}:3306/order    username: ${DB_USER:order}    password: ${DB_PASSWORD}    hikari:      pool-name: order-hikari      maximum-pool-size: 20      connection-timeout: 2000  jpa:    open-in-view: false  data:    redis:      host: ${REDIS_HOST:localhost}      timeout: 500ms应用配置：app:  downstream:    inventory:      timeout: 2s      retry: 2  feature:    new-order-flow: false4. Actuatormanagement:  endpoints:    web:      exposure:        include: health,info,prometheus,metrics  endpoint:    health:      probes:        enabled: true      show-details: never端点：            端点      用途                  /actuator/health      健康              /actuator/health/liveness      存活              /actuator/health/readiness      就绪              /actuator/prometheus      指标              /actuator/conditions      自动装配报告              /actuator/env      环境配置              /actuator/beans      Bean 信息              /actuator/threaddump      线程 dump      生产谨慎暴露 env、beans、threaddump。5. Web 接口模板@RestController@RequestMapping("/api/orders")public class OrderController {    @PostMapping    @ResponseStatus(HttpStatus.CREATED)    public OrderView create(@RequestHeader("Idempotency-Key") String key,                            @Valid @RequestBody CreateOrderRequest request) {        return orderService.create(request.toCommand(key));    }    @GetMapping("/{id}")    public OrderView get(@PathVariable Long id) {        return orderService.get(id);    }}全局异常：@RestControllerAdvicepublic class ApiExceptionHandler {    @ExceptionHandler(MethodArgumentNotValidException.class)    @ResponseStatus(HttpStatus.BAD_REQUEST)    public ErrorResponse handleValidation(MethodArgumentNotValidException ex) {        return new ErrorResponse("VALIDATION_ERROR", "invalid request");    }}6. 事务速查传播行为：            类型      说明                  REQUIRED      默认，有则加入，无则新建              REQUIRES_NEW      挂起当前，新建事务              NESTED      保存点嵌套              SUPPORTS      有则加入              NOT_SUPPORTED      挂起并非事务执行              NEVER      有事务则异常              MANDATORY      必须已有事务      失效检查：自调用？public 方法？异常被吞？rollbackFor？Bean 被 Spring 管理？多线程执行？代理和事务管理器配置正确？7. Feign 配置spring:  cloud:    openfeign:      client:        config:          inventory-service:            connect-timeout: 500            read-timeout: 2000            retryer: never接口：@FeignClient(name = "inventory-service", path = "/api/inventories")public interface InventoryClient {    @GetMapping("/{id}")    InventoryView get(@PathVariable Long id);}8. Resilience4jresilience4j:  circuitbreaker:    instances:      inventory:        sliding-window-size: 100        minimum-number-of-calls: 20        failure-rate-threshold: 50        wait-duration-in-open-state: 10s  ratelimiter:    instances:      createOrder:        limit-for-period: 100        limit-refresh-period: 1s状态：CLOSED -&gt; OPEN -&gt; HALF_OPEN9. 网关路由spring:  cloud:    gateway:      server:        webflux:          routes:            - id: order              uri: lb://order-service              predicates:                - Path=/api/orders/**常用谓词：PathMethodHeaderQueryHostWeight10. Kafka 配置生产者：spring:  kafka:    producer:      bootstrap-servers: kafka:9092      acks: all      properties:        enable.idempotence: true消费者：spring:  kafka:    consumer:      group-id: order-service      enable-auto-commit: false治理：Outbox幂等表分区 key重试 topic死信 topiclag 告警11. 测试注解            注解      用途                  @SpringBootTest      完整集成测试              @WebMvcTest      Web 切片              @DataJpaTest      JPA 切片              @Testcontainers      启动容器              @ServiceConnection      自动连接测试容器              @ActiveProfiles      指定 profile              @MockBean      替换 Bean              @Sql      执行 SQL      Maven 命令：mvn testmvn verify12. 容器启动参数exec java \  -XX:+UseContainerSupport \  -XX:InitialRAMPercentage=60.0 \  -XX:MaxRAMPercentage=60.0 \  -XX:ActiveProcessorCount=4 \  -XX:MaxMetaspaceSize=512m \  -XX:MaxDirectMemorySize=1g \  -Xlog:gc*,safepoint:file=/logs/gc.log:time,uptime,level,tags \  -jar /app/app.jar探针：/actuator/health/liveness/actuator/health/readiness13. 优雅停机server.shutdown=gracefulspring.lifecycle.timeout-per-shutdown-phase=30sK8s：terminationGracePeriodSeconds &gt; 应用等待时间14. 排查命令# JVMjcmd &lt;pid&gt; VM.command_linejcmd &lt;pid&gt; VM.flagsjcmd &lt;pid&gt; Thread.printjcmd &lt;pid&gt; GC.heap_info# Linuxtop -H -p &lt;pid&gt;ss -lntpvmstat 1日志级别：logging.level.org.springframework.web=DEBUGlogging.level.org.springframework.transaction=DEBUG15. 常见故障判断            现象      方向                  Bean not found      扫描、条件、名字              事务不回滚      自调用、异常、传播              启动慢      Runner、类扫描、CPU              接口慢      SQL、下游、锁、GC              线程池拒绝      下游慢、队列满、流量              OOMKilled      容器总内存              服务找不到      namespace、group、网络              发布 502      摘流和优雅停机              消息重复      至少一次，需幂等              配置不生效      优先级、profile、缩进</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。掌握 Spring Boot 与 Spring Cloud，不只是会写注解和搭服务，而是能设计稳定系统、读懂框架边界、治理研发流程，并在故障中沉淀体系化经验。35.1 能力模型            层级      能力                  入门      完成 Web、配置、数据库、测试              熟练      理解 IoC、AOP、事务、自动装配              进阶      设计缓存、消息、异步和分布式一致性              高阶      治理微服务、安全、观测、发布和容量              专家      读源码、做架构取舍、建设团队规范      每个层级都要有可验证产出，而不是只看工作年限。35.2 学习路线第一阶段：工程能力目标：  独立创建 Spring Boot 服务；  规范 REST API；  使用数据库事务；  写单元和集成测试；  接入日志和指标。产出：一个可部署的小型业务系统。第二阶段：框架原理目标：  理解 Bean 生命周期；  解释 AOP 和事务代理；  掌握配置加载优先级；  阅读自动装配源码；  编写 Starter。产出：一个带完整测试的 Starter。第三阶段：分布式能力目标：  服务拆分；  注册发现和配置治理；  网关与灰度；  熔断限流降级；  Outbox、Saga、幂等；  事件驱动架构。产出：一个订单交易微服务项目。第四阶段：生产能力目标：  建立可观测性；  做容量和性能压测；  无损发布；  安全治理；  故障预案和演练；  多环境和多机房。产出：一套上线运行手册。35.3 推荐实践  构造器注入；  配置对象化；  Controller 薄；  外部调用必有超时；  写操作幂等；  消息消费有死信；  日志指标 trace 三件套；  数据库脚本版本化；  发布可回滚；  测试分层稳定。这些规则比引入任何新框架更能降低系统复杂度。35.4 架构判断常见取舍：            问题      判断                  单体还是微服务      业务规模、团队和边界是否需要              同步还是异步      是否需要实时结果和强反馈              强一致还是最终一致      业务风险和补偿成本              注册中心还是 K8s Service      部署形态和治理能力              Spring Cloud 还是 Mesh      能力重叠与团队掌控度              JPA 还是 MyBatis      模型复杂度和 SQL 控制度      架构没有标准答案，只有约束下的合理选择。35.5 深入源码建议主线：SpringApplication.runrefreshBean lifecycleBeanPostProcessorAOP proxyTransaction interceptorDispatcherServletAutoConfigurationActuator metrics源码学习输出：  时序图；  关键接口；  扩展点；  版本差异；  最小实验；  应用到项目的改进。不要为了背源码而读源码。35.6 关注演进            方向      关注点                  Spring Boot 3.x      Jakarta、Native、可观测性              Spring Framework 6      RestClient、HTTP Interface              虚拟线程      阻塞 IO 模型变化              GraalVM Native Image      启动速度、反射配置              Project CRaC      快照启动              Service Mesh      平台流量治理              OpenTelemetry      统一观测协议      学习新特性时问：  解决什么问题；  引入什么限制；  与当前版本兼容性；  迁移成本；  有没有可验证收益。35.7 团队治理工程规范：1. 统一依赖 BOM2. 统一异常和日志3. 统一测试模板4. 统一部署模板5. 统一安全策略6. 统一 API 契约7. 统一中间件接入8. 统一发布回滚平台能力：  服务目录；  owner 信息；  拓扑图；  指标看板；  日志查询；  trace 查询；  变更审计；  发布编排。35.8 故障复盘库每类故障沉淀：现象影响时间线证据根因修复预防演练方案常见主题：  事务失效；  连接池耗尽；  缓存击穿；  消息重复；  注册中心抖动；  无损发布失败；  CPU throttling；  OOMKilled；  依赖版本冲突；  配置漂移。35.9 12 周提升计划            阶段      内容                  1-2 周      IoC、生命周期、注入、配置              3 周      AOP、事务、数据访问              4-5 周      Web、测试、Starter              6-7 周      微服务、网关、注册配置              8 周      熔断限流、分布式事务、事件              9-10 周      观测、性能、故障排查              11 周      容器、发布、优雅上下线              12 周      项目复盘和内部分享      每周至少完成一个可运行实验。35.10 最终建议  先写清楚业务边界，再引入技术栈；  先有可观测性，再谈优化；  先做幂等和补偿，再谈异步；  先压测，再扩容；  先回滚方案，再发布；  先版本兼容，再数据库变更；  先统一规范，再推广平台；  先保留证据，再重启。Spring 生态很大，但主线一直没变：控制反转管理对象，AOP 处理横切，Boot 简化装配，Cloud 治理分布式，生产体系保证系统真的稳定运行。本章小结大师之路是工程能力、框架原理、分布式设计、生产治理和团队规范的持续叠加。通过可运行项目、源码实验、故障复盘和平台化治理，把知识变成稳定系统能力。技术选型始终回到业务目标、团队能力和长期维护成本。思考题  你当前在能力模型中的哪一层？  哪个生产故障可以整理成团队案例？  你的团队最缺少哪条工程规范？  Spring Cloud 和 Service Mesh 如何取舍？  下一个 12 周学习计划是什么？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。本章整理 Spring Boot 与 Spring Cloud 高频面试题。回答时先给结论，再讲原理、场景和边界，最好结合一次真实故障或调优数据。34.1 IoC 与 DI问题：IoC 和 DI 是什么关系？回答：IoC 是设计思想：对象创建和装配的控制权交给容器。DI 是实现方式：容器通过构造器、setter、字段等方式注入依赖。加分点：  BeanDefinition 是图纸；  BeanFactory 是基础容器；  ApplicationContext 增加事件、资源、环境能力；  构造器注入便于测试并暴露职责膨胀。34.2 Bean 生命周期问题：Bean 生命周期包含哪些阶段？回答：BeanDefinition 解析  -&gt; 实例化  -&gt; 属性填充  -&gt; Aware 回调  -&gt; before initialization  -&gt; @PostConstruct / afterPropertiesSet / initMethod  -&gt; after initialization  -&gt; Bean 就绪  -&gt; 销毁回调加分点：  BeanPostProcessor 是 AOP 的基础；  代理通常在初始化后生成；  @PreDestroy、DisposableBean、destroyMethod 的顺序；  singleton 和 prototype 生命周期差异。34.3 循环依赖问题：Spring 如何处理循环依赖？回答：  三级缓存针对单例 setter/字段注入；  提前暴露早期引用；  构造器循环依赖不能解决；  prototype 不能解决；  Spring Boot 2.6+ 默认禁止。正确表达：循环依赖能被机制处理，不代表设计合理。更应拆分职责、引入事件或第三方编排服务。34.4 AOP 失效问题：为什么 @Transactional 自调用失效？回答：调用方拿到的是代理对象。代理对象进入目标对象执行 create()。create() 内部 this.validate() 是目标对象调用，不再经过代理。解决：  拆分 Bean；  外部调用；  AopContext.currentProxy()；  重新设计职责。其他失效：            场景      原因                  private 方法      代理无法拦截              final 方法      CGLIB 无法覆盖              异常被吞      拦截器看不到              受检异常      默认不回滚              多线程      事务绑定线程      34.5 Spring Boot 自动装配问题：自动装配如何工作？回答：  @EnableAutoConfiguration 导入选择器；  读取自动配置类清单；  Spring Boot 3 使用 AutoConfiguration.imports；  通过条件注解过滤；  按 @AutoConfigureBefore/After 排序；  注册 BeanDefinition。常见条件：@ConditionalOnClass@ConditionalOnMissingBean@ConditionalOnProperty@ConditionalOnWebApplication排查方式：--debug 输出 conditions report。34.6 配置优先级问题：Spring Boot 配置谁优先？回答方向：命令行通常优先环境变量次之配置中心视客户端实现本地 profile 文件默认配置不要背死顺序。强调真实结果由 PropertySource 顺序决定，可用 /actuator/env 或 Environment 输出确认。34.7 事务传播问题：REQUIRES_NEW 和 NESTED 有什么区别？            行为      说明                  REQUIRES_NEW      挂起当前事务，开启独立新事务              NESTED      在当前事务内使用保存点，局部回滚      风险：  REQUIRES_NEW 占用第二个连接；  高并发可能耗尽连接池；  NESTED 依赖数据库保存点支持；  外层回滚会影响嵌套事务。34.8 Starter 设计问题：如何设计一个 Starter？回答：  拆分 core、api、autoconfigure、starter；  不扫描用户包；  使用 @ConfigurationProperties；  条件装配提供默认 Bean；  允许用户覆盖；  提供超时和资源释放；  暴露指标和健康检查；  注册文件使用正确版本格式；  提供配置提示；  编写自动配置测试。34.9 微服务拆分问题：如何拆微服务？回答框架：1. 梳理业务流程和通用语言2. 识别限界上下文3. 确定数据所有权4. 评估变更频率和团队边界5. 从粗粒度服务开始6. 建立契约和观测7. 逐步独立数据库反模式：  按技术层拆；  共享数据库；  小团队拆过多服务；  两个服务总是一起发布；  没有明确 owner。34.10 服务发现问题：注册中心怎么选？回答维度：  AP/CP 取舍；  健康检查模型；  多环境隔离；  权限；  运维成本；  与 K8s 或云平台集成；  团队已有生态。关键结论：服务发现通常偏 AP，消费者本地缓存服务列表；即使有注册中心，也必须保留超时、重试和熔断。34.11 网关职责问题：网关应该做什么，不该做什么？应该：  路由；  认证；  限流；  观测；  灰度；  安全 Header；  TLS 终止。不应该：  复杂业务规则；  数据库访问；  聚合复杂业务数据；  频繁发布的核心逻辑。34.12 熔断限流问题：熔断器三个状态是什么？CLOSED -&gt; OPEN -&gt; HALF_OPEN -&gt; CLOSED/OPEN参数：  统计窗口；  最小请求数；  失败率；  慢调用比例；  打开时长；  半开探测数。强调：  先有超时；  重试要幂等且有预算；  fallback 必须记录指标；  降级要符合业务语义。34.13 分布式事务问题：下单跨服务如何保证一致性？回答：  先定义状态机；  本地操作用本地事务；  事件可靠投递用 Outbox；  每一步幂等；  失败按 Saga/TCC 补偿；  定期对账；  异常进入人工处理。不推荐只回答“用 Seata”。框架只是实现手段，业务状态和补偿设计才是核心。34.14 优雅停机问题：K8s 下 Spring Boot 如何无损发布？回答：readiness 探针摘流  -&gt; 收到 SIGTERM  -&gt; server.shutdown=graceful  -&gt; 停止接收新请求  -&gt; 等待在途请求  -&gt; 关闭线程池和消费者  -&gt; 退出配套：  preStop sleep 等摘流传播；  terminationGracePeriodSeconds 大于应用等待；  消息处理完再提交 offset；  发布压测验证。34.15 性能排查问题：接口 P99 突然变高怎么排查？回答：1. 确认影响范围和时间2. 看 QPS 和错误率3. trace 定位慢在哪个 span4. 检查 DB / Redis / 下游5. 检查线程池和连接池6. 检查 GC 和 CPU throttling7. 关联发布和配置变更8. 修复后验证加分点：区分平均值和 P99，指出慢请求采样要保留。34.16 容器内存问题：Pod memory limit 2Gi，-Xmx 应该设多大？回答：不能等于 limit，因为还有：  Metaspace；  线程栈；  Direct memory；  CodeCache；  Native；  OS 余量。可用：-XX:MaxRAMPercentage=60.0再根据监控和 OOMKilled 情况调整。本章小结Spring 面试要重点讲清 IoC、Bean 生命周期、AOP、事务、自动装配、配置、Starter、微服务拆分、服务治理、分布式一致性和生产排障。高质量回答应包含场景、边界、证据和验证，而不是罗列注解。思考题  哪些问题回答时必须说明 Spring Boot 版本？  如何用自动装配报告排查 Bean 未创建？  如何把一次事务失效讲成完整案例？  服务拆分如何证明不是过度设计？  微服务面试如何展示生产经验？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。本章以“订单交易系统”为例，把 Spring Boot、Spring Cloud、数据访问、缓存、消息、观测和发布整合成一个可落地的工程方案。33.1 业务流程用户下单  -&gt; 校验商品和价格  -&gt; 预占库存  -&gt; 创建订单  -&gt; 发起支付  -&gt; 支付成功  -&gt; 扣减库存  -&gt; 创建履约单  -&gt; 发送通知非功能目标：            目标      指标                  下单 P99      &lt;= 300ms              可用性      &gt;= 99.95%              消息可靠性      至少一次 + 幂等              发布      无损滚动              排障      trace 全链路              回滚      5 分钟内      33.2 服务划分gateway-serviceorder-serviceproduct-serviceinventory-servicepayment-servicenotification-service数据所有权：            服务      数据                  order      orders、order_events              product      products、prices              inventory      stock、stock_logs              payment      payments、payment_logs              notification      notifications      服务间：  下单前价格和库存用 API；  下单后流程用事件；  查询列表读订单投影；  报表进入数仓。33.3 订单状态机CREATED  -&gt; STOCK_RESERVED     -&gt; PAYING        -&gt; PAID           -&gt; FULFILLING              -&gt; COMPLETED     -&gt; CANCELLEDPAYING -&gt; CLOSEDPAID -&gt; REFUNDING -&gt; REFUNDED表结构：create table orders (  id bigint primary key,  order_no varchar(32) not null unique,  user_id varchar(64) not null,  amount decimal(18,2) not null,  status varchar(32) not null,  idempotency_key varchar(64) not null unique,  created_at datetime(3) not null,  updated_at datetime(3) not null,  key idx_user_time(user_id, created_at));状态迁移必须用条件更新：update ordersset status = 'PAID', updated_at = now(3)where id = ? and status = 'PAYING';33.4 下单接口@RestController@RequestMapping("/api/orders")public class OrderController {    @PostMapping    @ResponseStatus(HttpStatus.CREATED)    public OrderView create(@RequestHeader("Idempotency-Key") String idempotencyKey,                            @Valid @RequestBody CreateOrderRequest request) {        return orderService.create(request.toCommand(idempotencyKey));    }}请求：public record CreateOrderRequest(        @NotBlank String userId,        @NotNull @Positive Long skuId,        @Min(1) @Max(100) int quantity) {    public CreateOrderCommand toCommand(String idempotencyKey) {        return new CreateOrderCommand(idempotencyKey, userId, skuId, quantity);    }}幂等键由客户端生成，服务端唯一约束兜底。33.5 下单服务@Servicepublic class OrderService {    @Transactional    public OrderView create(CreateOrderCommand command) {        if (repository.existsByIdempotencyKey(command.idempotencyKey())) {            return repository.findByIdempotencyKey(command.idempotencyKey()).toView();        }        ProductSnapshot product = productClient.get(command.skuId());        Reservation reservation = inventoryClient.reserve(                new ReserveRequest(command.orderNo(), command.skuId(), command.quantity()));        Order order = Order.create(command, product, reservation);        repository.save(order);        outboxRepository.save(OrderCreatedEvent.outbox(order));        return order.toView();    }}注意：  库存预占接口必须幂等；  本地事务保护订单和 outbox；  支付在订单确认后发起；  所有外部调用有超时；  失败要有明确状态或补偿。33.6 库存设计表：create table stock (  sku_id bigint primary key,  available int not null,  frozen int not null,  version bigint not null default 0);预占：update stockset available = available - ?, frozen = frozen + ?, version = version + 1where sku_id = ? and available &gt;= ?;确认：update stockset frozen = frozen - ?where sku_id = ? and frozen &gt;= ?;释放：update stockset available = available + ?, frozen = frozen - ?where sku_id = ? and frozen &gt;= ?;同时记录 stock_log，用于幂等、审计和对账。33.7 Outbox 表create table outbox_events (  event_id varchar(64) primary key,  event_type varchar(100) not null,  aggregate_id varchar(64) not null,  payload json not null,  status varchar(20) not null,  retry_count int not null default 0,  next_retry_at datetime(3) null,  created_at datetime(3) not null,  sent_at datetime(3) null,  key idx_status_time(status, created_at));投递任务：1. 查询 PENDING 且到达 next_retry_at2. 按 aggregateId 顺序发送3. 成功标记 SENT4. 失败递增 retry_count5. 超过阈值标记 FAILED 并告警33.8 支付回调@PostMapping("/api/payments/callback")public void callback(@Valid @RequestBody PaymentCallback callback) {    paymentService.handle(callback);}处理：1. 验签2. 根据支付流水号查询本地记录3. 校验金额和币种4. 幂等更新支付状态5. 发布 PaymentSucceeded6. 返回成功如果金额不一致，不能直接更新为成功，应进入异常处理流程。33.9 缓存策略            数据      缓存      TTL                  商品详情      Redis + Caffeine      5min              用户基础信息      Redis      30min              库存数量      短缓存或实时      视业务              订单列表      不缓存或个性化      -      商品更新：更新 DB  -&gt; 删除 Redis  -&gt; 发布失效事件  -&gt; 实例删除本地缓存库存展示允许短暂近似值，下单扣减必须实时校验数据库。33.10 观测方案指标：order_create_totalorder_create_secondsorder_status_totalpayment_callback_totalinventory_reserve_secondsoutbox_pendingconsumer_lagsaga_compensation_total关键日志：orderIdorderNouserIdpaymentIdtraceIdstate transitionamount告警：  下单失败率；  下单 P99；  支付回调异常；  outbox 积压；  Saga 补偿异常；  库存负数或冻结异常。33.11 测试            层      测试                  单元      状态机、金额、库存规则              Web      参数校验、响应、异常              集成      MySQL、Redis、Kafka Testcontainers              契约      服务 API 和事件 schema              E2E      下单、支付、取消、退款              混沌      下游超时、DB 抖动、消息重复      必须覆盖：  重复 Idempotency-Key；  库存不足；  支付超时；  回调重复；  补偿失败；  消息乱序。33.12 部署配置：resources:  requests:    cpu: "1"    memory: "2Gi"  limits:    memory: "2Gi"发布：  数据库迁移；  服务金丝雀；  观察核心指标；  扩大流量；  保留回滚；  发布窗口避开高峰。本章小结订单系统实战的核心是状态机、幂等、数据所有权、Outbox 事件、库存补偿和全链路可观测。同步 API 用于实时校验，异步事件驱动后续流程，所有跨服务操作必须假设失败、重复和乱序，并通过对账兜底。思考题  为什么订单创建和 Outbox 写入要在同一事务？  库存预占为什么不能只依赖缓存？  支付回调如何防伪造和重复？  Saga 补偿失败如何处理？  订单系统至少要监控哪些指标？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。中间件集成不只是引入依赖，还要考虑连接池、超时、重试、认证、可观测性、故障隔离和数据安全。本章按常见中间件给出集成要点和治理清单。32.1 MySQL依赖：&lt;dependency&gt;    &lt;groupId&gt;com.mysql&lt;/groupId&gt;    &lt;artifactId&gt;mysql-connector-j&lt;/artifactId&gt;&lt;/dependency&gt;配置：spring:  datasource:    url: jdbc:mysql://mysql:3306/order?useSSL=true&amp;serverTimezone=Asia/Shanghai    username: ${DB_USER}    password: ${DB_PASSWORD}    hikari:      maximum-pool-size: 20      connection-timeout: 2000治理：  超时和最大连接数；  慢 SQL 监控；  读写分离策略；  事务边界；  连接池指标；  密钥管理；  迁移脚本版本化。32.2 Redis依赖：&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt;&lt;/dependency&gt;配置：spring:  data:    redis:      host: redis      port: 6379      password: ${REDIS_PASSWORD}      timeout: 500ms      lettuce:        pool:          max-active: 50          max-idle: 20          min-idle: 5治理：  key 命名；  TTL 策略；  大 key 扫描；  热 key 保护；  序列化兼容；  集群和主从拓扑；  命令延迟监控。32.3 Kafka依赖：&lt;dependency&gt;    &lt;groupId&gt;org.springframework.kafka&lt;/groupId&gt;    &lt;artifactId&gt;spring-kafka&lt;/artifactId&gt;&lt;/dependency&gt;生产者：spring:  kafka:    producer:      bootstrap-servers: kafka:9092      acks: all      properties:        enable.idempotence: true消费者：spring:  kafka:    consumer:      group-id: order-service      auto-offset-reset: earliest      enable-auto-commit: false治理：  Outbox；  幂等消费；  分区 key；  消费 lag；  重试 topic；  死信队列；  schema 版本。32.4 RocketMQ依赖：&lt;dependency&gt;    &lt;groupId&gt;org.apache.rocketmq&lt;/groupId&gt;    &lt;artifactId&gt;rocketmq-spring-boot-starter&lt;/artifactId&gt;&lt;/dependency&gt;配置：rocketmq:  name-server: rocketmq:9876  producer:    group: order-producer    send-message-timeout: 3000特点：  事务消息；  延迟消息；  顺序消息；  消费重试；  死信 topic；  广播和集群消费。使用顺序消息时要保证生产队列选择和消费串行逻辑一致。32.5 Elasticsearch依赖：&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-starter-data-elasticsearch&lt;/artifactId&gt;&lt;/dependency&gt;配置：spring:  elasticsearch:    uris: https://es:9200    username: ${ES_USER}    password: ${ES_PASSWORD}    connection-timeout: 1s    socket-timeout: 5s治理：  索引生命周期；  mapping 版本；  分片和副本；  写入批量大小；  查询超时；  慢查询；  数据同步链路。32.6 MongoDB配置：spring:  data:    mongodb:      uri: mongodb://mongo:27017/order治理：  索引设计；  文档大小；  写关注和读偏好；  副本集；  连接池；  慢查询；  schema 演进。适合灵活文档模型、日志轨迹、内容详情，不宜盲目替代关系数据库。32.7 RabbitMQ配置：spring:  rabbitmq:    host: rabbitmq    port: 5672    username: ${RABBIT_USER}    password: ${RABBIT_PASSWORD}    listener:      simple:        acknowledge-mode: manual        prefetch: 50治理：  exchange、queue、binding 版本化；  死信交换器；  惰性队列；  prefetch；  手动 ack；  队列积压告警。32.8 对象存储接口：public interface StorageClient {    String upload(String key, InputStream stream, long size, String contentType);    void download(String key, OutputStream out);    String presignedGetUrl(String key, Duration ttl);}治理：  key 设计；  生命周期；  分片上传；  权限；  CDN；  防盗链；  跨区域备份。不要把大文件读进内存再上传。32.9 邮件短信共同要求：  重试和幂等；  发送状态持久化；  模板版本；  频率限制；  退避和黑名单；  供应商降级；  敏感信息脱敏。异步发送：@Async("notifyExecutor")public void send(SmsMessage message) {}通知失败通常不阻塞主业务，但要有补偿和查询。32.10 集成清单1. 明确依赖版本矩阵2. 设置认证和密钥3. 配置连接池和超时4. 区分可重试和不可重试错误5. 提供健康检查6. 暴露指标7. 接入 trace8. 定义容量和限流9. 配置降级策略10. 编写集成测试和故障演练本章小结中间件集成要从数据模型、连接治理、故障语义、可观测性和安全五个层面设计。数据库关注事务和慢查询，缓存关注一致性和容量，消息关注可靠性和幂等，搜索关注索引生命周期，通知类中间件关注重试和降级。任何中间件都要纳入统一监控和故障演练。思考题  集成 Redis 时必须治理哪些风险？  Kafka 和 RocketMQ 的事务语义有何差异？  Elasticsearch mapping 如何安全演进？  对象存储为什么要流式处理？  中间件健康检查应包含什么级别？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。读 Spring 源码的目的不是背类名，而是理解框架如何管理 Bean、启动上下文、处理请求、实现事务代理和自动装配。源码浩大，必须从问题进入，沿着一次请求或一次启动流程走。31.1 准备源码获取源码：git clone https://github.com/spring-projects/spring-framework.gitgit clone https://github.com/spring-projects/spring-boot.git建议选择与项目相同的 release 分支。日常开发不必自己编译整个 Spring，可直接下载 source jar 或在 IDE 中自动关联源码。Maven 下载源码：mvn dependency:sources31.2 模块地图spring-core  |-- core  |-- beans  |-- context  |-- aop  |-- tx  |-- web  +-- webmvcspring-boot  |-- spring-boot  |-- spring-boot-autoconfigure  |-- spring-boot-actuator  +-- starters            模块      重点                  spring-beans      BeanDefinition、BeanFactory、生命周期              spring-context      事件、扩展点、refresh              spring-aop      代理、切点、Advisor              spring-tx      事务拦截器和传播行为              spring-webmvc      DispatcherServlet 请求链              spring-boot      SpringApplication 启动流程              autoconfigure      条件装配和 Starter      31.3 IoC 阅读路线入口：SpringApplication.run(OrderApplication.class, args);推荐路线：SpringApplication.run  -&gt; createApplicationContext  -&gt; refreshContext  -&gt; AbstractApplicationContext.refresh  -&gt; invokeBeanFactoryPostProcessors     -&gt; ConfigurationClassPostProcessor  -&gt; finishBeanFactoryInitialization  -&gt; DefaultListableBeanFactory.preInstantiateSingletons  -&gt; AbstractBeanFactory.doGetBean  -&gt; AbstractAutowireCapableBeanFactory.createBean重点问题：  BeanDefinition 从哪里来；  BeanPostProcessor 何时注册；  单例缓存如何工作；  依赖注入如何解析候选者；  Bean 生命周期回调顺序。31.4 Bean 生命周期阅读核心方法：AbstractAutowireCapableBeanFactory.createBean  -&gt; createBeanInstance  -&gt; populateBean  -&gt; initializeBean     -&gt; invokeAwareMethods     -&gt; applyBeanPostProcessorsBeforeInitialization     -&gt; invokeInitMethods     -&gt; applyBeanPostProcessorsAfterInitialization断点建议：  createBeanInstance：观察构造器选择；  populateBean：观察依赖注入；  initializeBean：观察初始化回调；  postProcessAfterInitialization：观察 AOP 代理；  destroySingleton：观察销毁流程。调试时可以在自定义 Bean 的构造器、@PostConstruct、@PreDestroy 打日志，把源码顺序和业务观察对应起来。31.5 AOP 阅读路线入口：@EnableAspectJAutoProxy路线：AnnotationAwareAspectJAutoProxyCreator  -&gt; findCandidateAdvisors  -&gt; buildAspectJAdvisors  -&gt; shouldSkip / findEligibleAdvisors  -&gt; createProxy     -&gt; JdkDynamicAopProxy     -&gt; ObjenesissCglibAopProxy  -&gt; DynamicAdvisedInterceptor.intercept  -&gt; ReflectiveMethodInvocation.proceed重点问题：  Aspect 注解如何解析成 Advisor；  代理何时生成；  Advice 执行链如何组织；  为什么自调用失效；  @Transactional 如何复用 AOP。31.6 事务源码入口：@EnableTransactionManagement路线：TransactionAutoConfiguration  -&gt; ProxyTransactionManagementConfiguration  -&gt; TransactionInterceptor  -&gt; TransactionAspectSupport.invokeWithinTransaction  -&gt; PlatformTransactionManager.getTransaction  -&gt; DataSourceTransactionManager.doBegin  -&gt; commit / rollback重点看：  事务属性解析；  传播行为处理；  rollbackFor 匹配；  连接绑定 ThreadLocal；  savepoint 创建；  挂起和恢复事务。31.7 Spring MVC 源码一次请求：DispatcherServlet.doService  -&gt; doDispatch  -&gt; HandlerMapping.getHandler  -&gt; HandlerAdapter.handle  -&gt; RequestMappingHandlerAdapter.invokeHandlerMethod  -&gt; InvocableHandlerMethod.invokeForRequest  -&gt; HandlerMethodReturnValueHandler  -&gt; HttpMessageConverter重点问题：  URL 如何映射到 HandlerMethod；  参数如何解析；  校验何时触发；  异常如何进入 HandlerExceptionResolver；  返回值如何序列化；  Filter、Interceptor、ControllerAdvice 的顺序。31.8 Spring Boot 自动装配源码入口：@EnableAutoConfiguration路线：AutoConfigurationImportSelector  -&gt; getAutoConfigurationEntry  -&gt; ImportAutoConfigurationImportSelector  -&gt; AutoConfigurationSorter  -&gt; ConditionEvaluator  -&gt; OnClassCondition / OnBeanCondition / OnPropertyConditionSpring Boot 3 中自动配置类从 AutoConfiguration.imports 读取；旧版本从 spring.factories 读取。重点问题：  候选配置如何过滤；  条件报告如何生成；  配置顺序如何排序；  用户 Bean 如何覆盖默认 Bean。31.9 高效调试技巧条件断点：beanName.equals("orderService")method.getName().equals("createOrder")request.getRequestURI().equals("/api/orders")辅助 Bean：@Componentpublic class LifecycleDebugBean {    @PostConstruct    public void afterProperties() {        System.out.println("order service initialized");    }    @PreDestroy    public void beforeDestroy() {        System.out.println("order service destroyed");    }}观察 Bean：applicationContext.getBeansOfType(Object.class).size();applicationContext.getBeanDefinitionNames();建议新建最小工程，只保留被研究的问题，避免业务代码干扰。31.10 阅读方法推荐流程：1. 提出一个具体问题2. 从入口断点确认调用链3. 画出时序图4. 标记关键扩展点5. 写一个最小复现实验6. 对比不同版本7. 记录结论和边界不要试图从第一个类线性读完所有源码。Spring 的价值在主流程和扩展模型，理解这两层后，其余代码会更容易定位。本章小结Spring 源码阅读应从启动、Bean 生命周期、AOP、事务、MVC 和自动装配六条主线进入。先通过断点和日志观察行为，再阅读实现，把调用链画成图，并用最小实验验证理解。版本差异是源码学习中必须持续关注的边界。思考题  refresh() 中哪些阶段最重要？  AOP 代理是在哪个生命周期阶段生成的？  @Transactional 的核心入口是哪个类？  一次 REST 请求经过哪些核心组件？  如何验证自动配置条件为什么不匹配？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。多环境的目标不是拥有 dev、test、prod 三个名字，而是让配置、数据、权限、发布和验证流程有清晰边界，同时尽量保持生产相似度。30.1 环境分层            环境      目标                  local      开发自测              dev      联调              test / qa      功能和集成              staging / pre-prod      生产预演              prod      线上服务              dr      灾备      核心要求：  命名规范统一；  配置隔离；  数据隔离；  权限隔离；  流量隔离；  监控隔离；  发布策略不同。30.2 配置策略application.yml        公共默认application-dev.yml    开发差异application-test.yml   测试差异application-prod.yml   生产差异配置中心 namespace      环境隔离原则：  默认值面向安全，不是面向生产；  生产必需键显式配置；  环境地址来自环境变量；  密钥不落仓库；  配置变更可回滚；  每个配置有 owner。30.3 数据策略            环境      数据                  local      本地容器或内嵌模拟              dev      结构化假数据              test      可重建测试数据              staging      脱敏生产相似数据              prod      真实数据      禁止：  测试环境连接生产库；  生产数据未脱敏复制；  共享 Redis namespace；  测试 topic 和生产 topic 混用；  定时任务跨环境写同一数据源。30.4 权限治理            环境      开发权限                  local      完全              dev      较高              test      按需              staging      有限              prod      审批和审计      生产权限：  最小化；  审批；  审计；  时间窗口；  操作录像或命令记录；  禁止通用共享账号。30.5 环境漂移漂移来源：  手工改配置；  生产 hotfix 未回流；  数据库脚本遗漏；  环境资源不一致；  中间件版本不同；  依赖版本不同。治理：  配置 Git 化；  IaC 管环境；  数据库版本工具；  环境巡检；  制品同源；  版本矩阵；  自动 diff。30.6 冒烟与环境验证staging 发布后验证：1. 健康检查2. 核心接口3. 登录流程4. 支付沙箱5. 消息收发6. 数据库迁移7. 回滚演练8. 监控和告警自动化冒烟应接入发布流水线，失败自动阻断或暂停。30.7 环境监控每个环境都需要基础监控，但告警级别不同：            环境      告警                  dev      低噪声              test      阻断测试时通知              staging      严格，模拟生产              prod      全量关键告警      staging 与生产监控指标应保持一致，方便发布前对比。30.8 多机房与区域Region  |-- Zone A  |   |-- Service instances  |   |-- DB primary  |-- Zone B      |-- Service instances      |-- DB replica治理点：  流量就近；  数据复制延迟；  failover 策略；  配置中心可用性；  服务发现跨机房视图；  发布按区域灰度；  故障演练。跨机房写一致性和本机房读性能往往需要权衡。30.9 环境成本优化方式：  非生产环境定时启停；  测试数据保留周期；  日志采样；  trace 采样；  复用云资源；  按需创建 preview 环境；  清理孤儿资源。preview 环境适合分支联调，但必须自动回收，避免资源泄漏。本章小结多环境治理要保证配置、数据、权限、流量和监控隔离，同时让 staging 尽量接近生产。治理环境漂移比增加环境数量更重要。生产权限和变更必须审批审计，非生产资源要有生命周期管理。思考题  staging 和 test 的差异是什么？  如何避免环境配置漂移？  哪些数据不能直接复制到测试环境？  多机房流量就近有哪些代价？  如何治理 preview 环境资源泄漏？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。优雅上线确保实例准备好才接流量；优雅下线确保实例停止前排空流量、处理完任务并释放资源。否则发布时会出现连接拒绝、502、请求中断和消息重复。29.1 问题场景上线：Pod 启动完成  -&gt; 端口已监听  -&gt; 但缓存未预热 / 数据源未就绪  -&gt; 被导入流量  -&gt; 请求超时下线：Pod 收到 SIGTERM  -&gt; 立即关闭连接  -&gt; 在途请求失败  -&gt; 消费位移未提交  -&gt; 任务未完成29.2 启动就绪Spring Boot 就绪事件：@Componentpublic class WarmUpRunner implements ApplicationRunner {    @Override    public void run(ApplicationArguments args) {        warmUpLocalCache();    }}要求：  readiness 在预热完成后通过；  慢预热异步化或分批；  外部依赖检查不阻塞过久；  启动超时合理；  失败策略明确。29.3 Kubernetes 探针startupProbe:  httpGet:    path: /actuator/health/liveness    port: 8080  periodSeconds: 2  failureThreshold: 60readinessProbe:  httpGet:    path: /actuator/health/readiness    port: 8080  periodSeconds: 2发布顺序：1. 新 Pod 启动2. startup 通过3. readiness 通过4. Service 导流量5. 旧 Pod 缩容maxUnavailable: 0 可以避免发布过程容量下降。29.4 优雅停机配置Spring Boot：server.shutdown=gracefulspring.lifecycle.timeout-per-shutdown-phase=30sKubernetes：terminationGracePeriodSeconds: 45流程：SIGTERM  -&gt; Pod Endpoint 被移除  -&gt; readiness 变为 not ready  -&gt; 停止接收新请求  -&gt; 等待在途请求  -&gt; 关闭 Bean 和线程池  -&gt; 进程退出应用总等待时间必须小于 terminationGracePeriodSeconds，留出余量。29.5 preStop由于 Endpoint 更新和负载均衡刷新有延迟，可增加：lifecycle:  preStop:    exec:      command: ["sh", "-c", "sleep 5"]作用：  等待服务发现摘流；  避免旧实例仍被调用；  给网关和客户端缓存同步时间。sleep 只是简单方案，更完善方案由平台注入下线钩子。29.6 关闭线程池@PreDestroypublic void shutdown() throws InterruptedException {    executor.shutdown();    if (!executor.awaitTermination(20, TimeUnit.SECONDS)) {        executor.shutdownNow();    }}任务要求：  支持中断；  有执行时长上限；  未完成任务持久化或记录；  幂等可恢复；  关闭日志记录剩余任务数。29.7 消费者下线流程：1. 停止拉取新消息2. 处理完当前批次3. 提交 offset4. 关闭 consumer注意：  处理超时必须小于总停机时间；  位移提交失败会重复消费；  消费必须幂等；  不能在处理一半时 shutdownNow；  长事务要拆批。29.8 定时任务下线要求：  停止调度新任务；  正在执行的任务有最大时长；  分布式锁要能释放；  执行状态持久化；  未完成任务可恢复。ShedLock 示例：@Scheduled(cron = "0 */5 * * * *")@SchedulerLock(name = "settlement", lockAtMostFor = "10m")public void settle() {}停机时不要强杀，应等待当前分片完成或记录断点。29.9 验证发布压测：1. 持续压测请求2. 滚动发布服务3. 观察错误率4. 观察连接拒绝5. 观察消息重复6. 观察日志和 trace验收标准：  发布期间 5xx 不增加；  无 Connection refused；  无未完成请求中断；  消息无丢失；  定时任务无重复执行；  Pod 正常退出而不是 SIGKILL。本章小结优雅上下线是发布稳定性的基础。上线依赖 readiness 和预热策略，下线依赖 SIGTERM、摘流延迟、请求排空、线程池关闭、消息位移提交和任务状态持久化。总停机时间必须小于平台宽限期，并通过发布压测验证。思考题  为什么端口可访问不等于应用可服务？  preStop sleep 解决什么问题？  优雅停机时间如何设置？  消费者下线时为什么要先停止拉取？  如何验证发布过程零请求失败？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。容器化让 Spring Boot 应用以一致环境运行，Kubernetes 提供调度、扩缩容、发布和服务发现能力。云原生不是把 jar 放进容器，而是围绕健康检查、资源、配置、弹性和可观测重新设计运行方式。28.1 Dockerfile简单示例：FROM eclipse-temurin:21-jre-alpineWORKDIR /appCOPY target/app.jar /app/app.jarENV TZ=Asia/Shanghai \    JAVA_OPTS=""EXPOSE 8080ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -jar /app/app.jar"]最佳实践：  使用多阶段构建；  镜像最小化；  使用非 root 用户；  不把 secret 写入镜像；  exec 确保 Java 是 PID 1；  固定基础镜像版本；  扫描漏洞。28.2 JVM 容器参数java -XX:+UseContainerSupport \  -XX:InitialRAMPercentage=60.0 \  -XX:MaxRAMPercentage=60.0 \  -XX:ActiveProcessorCount=4 \  -jar /app/app.jar内存预算：容器 limit  &gt;= Heap   + Metaspace   + Thread stacks   + Direct memory   + CodeCache   + Native   + 余量若 MaxRAMPercentage=75 且 Netty、压缩缓存、大量线程同时存在，可能仍会 OOMKilled，需要按应用实测。28.3 Kubernetes DeploymentapiVersion: apps/v1kind: Deploymentmetadata:  name: order-servicespec:  replicas: 3  strategy:    type: RollingUpdate    rollingUpdate:      maxUnavailable: 0      maxSurge: 1  template:    spec:      containers:        - name: order          image: registry.example.com/order-service:1.8.0          ports:            - containerPort: 8080          resources:            requests:              cpu: "1"              memory: "2Gi"            limits:              memory: "2Gi"内存 request 和 limit 通常保持一致，避免调度与实际限制不一致。28.4 探针startupProbe:  httpGet:    path: /actuator/health/liveness    port: 8080  failureThreshold: 30  periodSeconds: 2livenessProbe:  httpGet:    path: /actuator/health/liveness    port: 8080readinessProbe:  httpGet:    path: /actuator/health/readiness    port: 8080区别：            探针      失败结果                  startup      延长启动判断              liveness      重启容器              readiness      摘除流量      liveness 检查不应包含外部依赖，否则下游故障会引发服务重启风暴。28.5 ConfigMap 与 SecretConfigMap：apiVersion: v1kind: ConfigMapmetadata:  name: order-configdata:  SPRING_PROFILES_ACTIVE: "prod"  PAY_TIMEOUT: "3s"生产建议使用平台密钥系统注入，而不是明文 Secret YAML。配置变更要有版本和回滚能力。28.6 Service 与 IngressapiVersion: v1kind: Servicemetadata:  name: order-servicespec:  selector:    app: order-service  ports:    - port: 80      targetPort: 8080Ingress 或 Gateway API 负责外部路由、TLS 和流量策略。服务间调用可选择 Kubernetes Service、注册中心或服务网格，保持一种主模型，避免策略分裂。28.7 HPAapiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata:  name: order-servicespec:  scaleTargetRef:    apiVersion: apps/v1    kind: Deployment    name: order-service  minReplicas: 3  maxReplicas: 20  metrics:    - type: Resource      resource:        name: cpu        target:          type: Utilization          averageUtilization: 65HPA 依赖指标可用。扩容还需考虑数据库连接总量、下游容量、冷启动、缓存预热、第三方限流和成本。28.8 Service Mesh网格提供 mTLS、流量拆分、重试超时、故障注入、遥测和权限策略。Spring Cloud 与网格能力可能重叠：            能力      Spring Cloud      Mesh                  服务发现      可用      平台提供              熔断      应用内      sidecar 策略              灰度      网关/注册元数据      流量规则              mTLS      按需实现      常见能力      选择时避免同一能力双层配置且语义不一致。28.9 资源与调度CPU：  request 影响调度；  limit 触发 throttling；  GC/JIT 线程受 CPU 感知影响；  突发流量要评估毛刺。常见问题：            现象      原因                  OOMKilled      内存超限              延迟毛刺      CPU throttling              Pod Pending      资源不足              反复重启      liveness 失败              发布卡住      PDB 或探针              调度不均      request 设置不合理      28.10 云原生清单1. 镜像不可变且可追溯2. 配置外部化3. secret 独立管理4. 健康探针正确5. 优雅停机6. 资源 request/limit 合理7. HPA 策略验证8. 日志输出 stdout 或挂载卷9. 指标和 trace 接入10. 发布可回滚11. PDB 保证最小可用12. 网络策略和安全策略本章小结容器与云原生部署要关注镜像、JVM 资源感知、探针、配置注入、自动扩缩容和优雅停机。Kubernetes 提供调度和发布能力，但应用仍需暴露正确健康状态并控制资源。引入服务网格时要明确与 Spring Cloud 治理能力的边界。思考题  为什么 ENTRYPOINT 中要使用 exec java？  内存 request 和 limit 为什么常保持一致？  liveness 为什么不应检查外部依赖？  HPA 扩容有哪些隐藏限制？  Spring Cloud 和 Service Mesh 能力如何分工？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。部署与发布的目标是让变更可控、可观察、可回滚。微服务环境下，一个功能可能涉及多个服务、数据库脚本、配置、消息契约和前端资源，需要完整的发布流程。27.1 制品管理Git commit  -&gt; CI 构建  -&gt; 镜像 / jar  -&gt; 版本号  -&gt; 制品仓库  -&gt; 部署系统镜像命名：registry.example.com/order-service:1.8.0registry.example.com/order-service:1.8.0-abc1234要求：  不可变制品；  同一镜像部署所有环境；  环境差异用配置注入；  记录 commit 和构建信息；  镜像签名和扫描；  不使用 latest 生产部署。27.2 CI 流水线阶段：CheckoutCompileUnit TestStatic AnalysisSecurity ScanPackageIntegration TestBuild ImagePush RegistryDeploy StagingE2E TestMaven 示例：mvn -B clean verifyGradle 示例：./gradlew clean check门禁：  测试通过；  代码扫描无严重问题；  依赖漏洞策略；  镜像扫描；  契约测试；  构建元数据完整。27.3 数据库变更原则：  向后兼容；  先加列后删列；  变更脚本可重复执行；  大表变更评估锁时间；  新旧代码兼容中间态；  回滚脚本明确；  生产变更备份。兼容流程：1. 添加 nullable 新列2. 旧代码继续读写旧列3. 新代码双写4. 迁移历史数据5. 校验一致6. 新代码读新列7. 多个版本后清理旧列27.4 发布策略            策略      说明                  Rolling      逐批替换              Blue/Green      两套环境切流量              Canary      少量流量验证              Feature Toggle      代码先上，功能后开              A/B      按用户分组体验      选择：  低风险小改动：滚动；  大版本或高风险：蓝绿；  核心链路：金丝雀；  未完成功能：开关控制；  数据不可逆变更：特殊审批和演练。27.5 金丝雀发布流程：1. 部署 1 个或 5% 实例2. 按用户/Header/权重导流量3. 观察错误率、延迟、业务指标4. 自动或人工判断5. 逐批扩大6. 全量后清理旧版本观察指标：error rateP50 / P95 / P99QPSGC pauseDB latencybusiness conversioncompensation count必须能一键回滚，且数据库兼容旧版本。27.6 回滚回滚内容：  应用版本；  配置；  路由规则；  数据修正；  消息消费逻辑；  前端资源。注意：  数据库 schema 不一定回滚；  已产生的消息和事件不会消失；  旧版本必须兼容新数据；  回滚也要验证和监控；  重大事故可先降级再回滚。27.7 部署检查清单1. 变更范围和依赖确认2. 数据库脚本演练3. 配置中心变更审核4. 镜像扫描通过5. 测试和契约测试通过6. 回滚方案可执行7. 监控和告警就绪8. 值班人员明确9. 发布窗口和暂停条件10. 用户通知暂停条件：  错误率上升；  P99 超阈值；  核心业务指标下降；  日志异常突增；  依赖故障；  发布系统异常。27.8 Spring Boot 健康探针健康组：management:  endpoint:    health:      probes:        enabled: true  health:    livenessState:      enabled: true    readinessState:      enabled: trueKubernetes：livenessProbe:  httpGet:    path: /actuator/health/liveness    port: 8080readinessProbe:  httpGet:    path: /actuator/health/readiness    port: 8080健康检查不应依赖非关键外部服务，否则下游抖动会导致实例被反复重启或摘流。27.9 发布平台能力平台应提供：  服务拓扑；  版本状态；  发布编排；  流量控制；  一键回滚；  变更审计；  指标对比；  日志查询；  审批流程；  发布锁。发布锁用于避免同一服务并发发布、数据库变更和应用发布冲突、重大节假日误发、依赖服务同时变更。本章小结部署与发布要把应用、配置、数据库和消息契约作为整体变更管理。生产使用不可变制品和统一镜像，通过 CI 门禁、兼容性数据库变更、金丝雀发布、指标观察和快速回滚控制风险。发布系统的核心能力是可审计、可暂停、可恢复。思考题  为什么生产禁止使用 latest 标签？  数据库加列和删列如何安全执行？  金丝雀发布观察哪些指标？  哪些情况下不能简单回滚？  如何设计发布暂停条件？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。故障排查的目标是快速恢复，其次才是找根因。先止血、保留证据、再修复和复盘。微服务故障往往跨多层，需要同时看入口、服务、依赖和平台。26.1 应急流程1. 发现和确认影响2. 通知相关团队3. 保留现场证据4. 尝试止血5. 验证恢复6. 根因分析7. 修复和发布8. 复盘和预防止血方式：  回滚版本；  扩容；  降级非核心功能；  切换流量；  重启实例；  限流；  修复配置；  数据库紧急处理。26.2 分层排查入口  -&gt; DNS / LB / Gateway     -&gt; Service        -&gt; Controller        -&gt; Service Layer        -&gt; DB / Cache / MQ / Downstream     -&gt; Platform        -&gt; Pod / Node / Network先确认影响范围：            范围      方向                  单实例      节点或实例问题              某服务所有实例      代码、配置、依赖              某机房      网络、依赖、云资源              全站      网关、DNS、核心依赖              某用户      权限、数据、缓存              某接口      SQL、下游、业务逻辑      26.3 接口 5xx排查：1. 网关访问日志2. 服务错误日志3. traceId 链路4. 异常类型和堆栈5. 发布和配置变更6. 依赖健康状态常见原因：            异常      方向                  NullPointerException      代码和数据边界              SQLSyntaxErrorException      发布脚本或实体映射              ConnectTimeoutException      下游不可用              Pool exhaustion      慢 SQL 或下游慢              ClassNotFoundException      依赖或打包问题              IllegalStateException      状态机和初始化顺序      26.4 接口慢查看分段耗时：总耗时网关耗时服务内部耗时SQL 耗时RPC 耗时Redis 耗时锁等待线程池排队常见瓶颈：  N+1 查询；  缺索引；  下游超时；  缓存失效；  大 JSON；  synchronized 竞争；  GC 停顿；  CPU throttling。26.5 服务不可用应用检查：curl http://host:8080/actuator/healthps -ef | grep javajcmd &lt;pid&gt; Thread.printss -lntp | grep 8080容器检查：kubectl get pod -o widekubectl describe pod &lt;pod&gt;kubectl logs &lt;pod&gt; --previouskubectl exec -it &lt;pod&gt; -- sh常见问题：            现象      原因                  OOMKilled      容器内存              CrashLoopBackOff      启动异常              ImagePullBackOff      镜像或凭证              Pending      资源或调度              readiness fail      健康检查              restart count 高      异常退出      26.6 线程池耗尽现象：  请求排队；  rejected 增长；  CPU 低；  大量线程 BLOCKED 或等待 IO。排查：jcmd &lt;pid&gt; Thread.print &gt; thread.txt方向：  下游超时缺失；  数据库连接不足；  锁竞争；  任务执行慢；  队列无界；  流量突增。处理：  限流；  降级；  设置超时；  隔离线程池；  修复慢依赖；  再评估容量。26.7 数据库故障现象：  连接池 pending；  SQL 超时；  锁等待；  主从延迟；  磁盘 IO 高。排查：show processlist;show engine innodb status;select * from information_schema.innodb_trx;处理：  kill 明确异常的长时间事务；  回滚问题 SQL；  限流写入；  切换读库；  联系 DBA；  事后补索引和优化。26.8 消息积压排查：  consumer lag；  消费耗时；  重试和死信；  分区分布；  消费实例数；  下游状态。处理：  扩容消费者，不能超过有效并发能力；  临时降级非关键逻辑；  修复慢消费；  跳过无效死信，必须记录；  提高下游吞吐；  观察消费幂等。26.9 保留证据必须保存：时间线告警截图监控快照trace 链接GC 日志线程 dump堆 dump应用日志网关日志平台事件变更记录聊天记录重启前优先保留：jcmd &lt;pid&gt; Thread.print &gt; thread.txtjcmd &lt;pid&gt; GC.heap_dump heap.hprofjcmd &lt;pid&gt; VM.flags &gt; flags.txt注意 heap dump 会 STW，要评估影响。26.10 故障复盘模板：故障编号时间影响时间线根因触发条件为什么没有提前发现为什么没有自动恢复应急过程改进项负责人和截止时间改进动作要具体、可测量、有负责人和截止时间。本章小结故障排查要先确认影响范围，再按入口、服务、依赖和平台分层定位。常见故障集中在 5xx、慢请求、线程池耗尽、数据库、消息积压和容器资源。应急时先止血并保留证据，恢复后通过复盘把发现和恢复能力产品化。思考题  为什么先止血再根因分析？  如何确认故障影响范围？  CPU 低但请求排队说明什么？  消息积压时如何安全扩容？  故障复盘最重要的输出是什么？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。性能调优要先定义目标、建立基线、找到瓶颈，再做最小改动并验证。盲目改配置容易把系统调得更复杂，却没有改善用户指标。25.1 性能指标            指标      说明                  QPS      每秒请求量              RT / P50 / P95 / P99      响应时间分布              error rate      错误率              saturation      饱和度              throughput      吞吐              cost per request      单请求成本              concurrency      并发      目标示例：在 4C8G、P99 &lt;= 200ms、错误率 &lt; 0.1% 的条件下，单实例支撑 1000 QPS。只说“更快”没有工程意义。25.2 USE 方法对每个资源看：Utilization 使用率Saturation 饱和度Errors 错误资源：  CPU；  内存；  线程池；  连接池；  数据库；  Redis；  网络；  磁盘 IO；  下游服务。25.3 压测准备：  生产相似数据量；  相似流量分布；  相同配置；  预热时间；  固定随机种子；  监控完整。工具：            工具      适用                  wrk / wrk2      HTTP 高并发              JMeter      复杂场景              Gatling      代码化场景              k6      云原生压测      报告：场景数据规模并发梯度QPSP50 / P95 / P99 / P999错误率CPU / Memory / GCDB / Redis / downstream 指标结论25.4 JVM 调优常见检查：1. GC 停顿分布2. Live 数据3. 分配速率4. 安全点等待5. 容器 CPU limit6. 线程数配置：java -Xms4g -Xmx4g \  -XX:+UseG1GC \  -XX:MaxMetaspaceSize=512m \  -Xlog:gc*,safepoint:file=/logs/gc.log:time,uptime,level,tags \  -jar app.jar如果 GC 总时间占比低，继续调 JVM 通常收益有限。25.5 数据库优化慢 SQL 处理：1. 确认扫描行数2. 查看 explain3. 检查索引4. 减少返回字段5. 拆深分页6. 避免函数导致索引失效7. 控制事务时长深分页优化：select id, user_id, amountfrom orderswhere id &gt; 100000order by idlimit 20;连接池：active == max 且 pending &gt; 0可能是连接池不足，也可能是 SQL 太慢。先看慢 SQL 和持连接时间。25.6 缓存优化检查：  命中率；  回源量；  热 key；  大 key；  序列化成本；  TTL 策略。热 key：  本地缓存；  key 打散；  请求合并；  限流；  预热。大 key：  拆分集合；  分页读取；  压缩；  避免整包 JSON 无界增长；  设置容量上限。25.7 线程与异步线程池监控：activepool sizequeue sizerejectedtask latency虚拟线程适合大量阻塞 IO 任务，但要关注：  pinning；  ThreadLocal 使用；  下游容量；  调度监控；  synchronized 长临界区。异步不是提升总量，只是改变等待方式。下游容量不变时，无限异步只会把压力后移。25.8 接口优化常见手段：            问题      优化                  N+1 查询      批量查询              大对象序列化      DTO 精简、压缩              重复查询      缓存              串行无依赖调用      并行化              每次创建重对象      复用              同步通知      异步化              全量加载      分页              复杂计算      预计算      并行调用示例：CompletableFuture&lt;ProductView&gt; productFuture =        CompletableFuture.supplyAsync(() -&gt; productClient.get(id), executor);CompletableFuture&lt;InventoryView&gt; inventoryFuture =        CompletableFuture.supplyAsync(() -&gt; inventoryClient.get(id), executor);CompletableFuture.allOf(productFuture, inventoryFuture).join();必须设置超时和异常处理。25.9 Web 容器调优Tomcat 配置：server:  tomcat:    threads:      max: 200      min-spare: 20    max-connections: 8192    accept-count: 100    connection-timeout: 5s不要只调大线程。若下游慢，线程越多只会放大故障。应同时：  设置下游超时；  隔离依赖；  限流；  减少阻塞；  扩容核心瓶颈。25.10 容量模型估算：目标 QPS = 峰值均值 * 峰值系数 * 增长系数实例数 = 目标 QPS / 单实例安全 QPS + 冗余冗余要满足：  一个实例故障仍承载流量；  发布期间可用容量不下降；  突发流量有余量；  HPA 冷启动时间；  数据库和中间件同步扩容。本章小结性能调优应从业务指标和资源饱和度入手，用压测建立基线，用 trace 和 profile 定位瓶颈。常见瓶颈通常在数据库、下游调用、缓存、线程池和序列化，而不是 JVM 参数。每次优化都要量化收益、验证副作用并更新容量模型。思考题  为什么 P99 比平均值更重要？  如何判断连接池是否是瓶颈？  什么情况下增加 Tomcat 线程无效？  虚拟线程解决什么问题？  如何设计容量冗余？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。安全不是登录页面的一个过滤器，而是输入校验、认证授权、密钥管理、依赖治理、日志脱敏、传输加密和审计的完整体系。24.1 认证与授权            概念      问题                  Authentication      你是谁              Authorization      你能做什么              Auditing      你做过什么      常见认证：  Session Cookie；  JWT；  OAuth2 / OIDC；  API Key；  mTLS；  云厂商签名。授权模型：            模型      适合                  RBAC      角色权限清晰              ABAC      属性条件复杂              ACL      资源级别控制              Policy      平台统一策略      24.2 Spring Security依赖：&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;&lt;/dependency&gt;配置：@Configuration@EnableWebSecuritypublic class SecurityConfig {    @Bean    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {        return http                .csrf(CsrfConfigurer::disable)                .authorizeHttpRequests(auth -&gt; auth                        .requestMatchers("/actuator/health").permitAll()                        .requestMatchers("/api/**").authenticated()                        .anyRequest().denyAll())                .sessionManagement(session -&gt; session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))                .build();    }}前后端分离 API 常用无状态 JWT，但 CSRF 禁用必须基于“不使用 Cookie 认证”等前提。24.3 JWT结构：header.payload.signature校验点：  签名算法是否允许；  签名是否有效；  exp 是否过期；  nbf 是否生效；  iss 是否匹配；  aud 是否匹配；  scope 是否足够；  key 是否轮转。JWT 无法主动撤销。登出、封禁、改密需要短过期时间、黑名单、刷新机制或版本号校验。24.4 OAuth2 与 OIDC常见模式：            模式      场景                  Authorization Code + PKCE      Web / 移动应用              Client Credentials      服务对服务              Device Code      设备登录              Refresh Token      换取新 access token      服务间调用：Order Service  -&gt; token endpoint  -&gt; access token  -&gt; Inventory Service注意：  client secret 不进前端；  token 不进日志；  scope 最小化；  使用 HTTPS；  校验 aud；  处理 token 过期；  缓存时间小于有效期。24.5 常见漏洞防护SQL 注入使用参数绑定：jdbcClient.sql("select * from users where id = :id").param("id", id);其他防护：            风险      防护                  XSS      输出转义、CSP、富文本白名单              CSRF      Cookie 认证启用 token              SSRF      URL 白名单、DNS 校验、禁内网访问              越权      服务端校验资源归属              反序列化      禁止任意类型              文件上传      校验魔数、大小、扩展名      水平越权示例：Order order = repository.findByIdAndUserId(id, currentUser.id())        .orElseThrow(OrderNotFoundException::new);仅依赖前端隐藏按钮不是授权。24.6 输入输出安全  服务端重复校验；  限制长度和范围；  白名单优先；  限制分页 size；  返回错误不泄露堆栈；  批量接口限制数量；  外部响应同样按不可信输入处理。批量限制：public record ImportRequest(        @NotEmpty @Size(max = 1000) List&lt;@Valid Item&gt; items) {}24.7 密钥管理禁止：app:  db-password: admin123推荐：环境变量VaultKMS云 Secret ManagerKubernetes Secret + 加密治理：  定期轮转；  最小权限；  密钥泄露立即吊销；  不同环境不同密钥；  日志脱敏；  代码仓库历史扫描。24.8 传输安全  外部强制 HTTPS；  内部按安全等级选择 mTLS 或服务网格；  证书有效期管理；  TLS 版本和密码套件符合公司规范；  HSTS；  Cookie 设置 Secure、HttpOnly、SameSite。server:  ssl:    enabled: true    key-store: /certs/server.p12    key-store-password: ${TLS_PASSWORD}24.9 依赖安全命令：mvn dependency:tree扫描工具：            工具      用途                  OWASP Dependency-Check      CVE 扫描              Trivy      镜像和依赖扫描              Snyk / Dependabot / Renovate      依赖升级      治理：  统一 BOM；  定期升级；  记录 CVE 决策；  无法升级时评估缓解措施；  镜像最小化；  SBOM 归档。24.10 安全审计必须记录：登录成功/失败权限变更敏感数据导出配置修改管理员操作支付相关操作安全策略变更审计日志字段：timeactoractionresourceresultsourceIptraceIdreason审计日志不可被普通用户修改，且要有独立存储和保留策略。本章小结Spring Security 提供认证授权框架，但安全边界要靠业务和平台共同保障。生产系统必须做服务端授权、输入校验、密钥管理、传输加密、依赖扫描和审计。安全设计要在需求阶段进入，而不是上线后补过滤器。思考题  JWT 如何处理登出和账号封禁？  哪些场景可以禁用 CSRF？  如何防止水平越权？  密钥应该放在哪里？  审计日志至少包含哪些字段？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。微服务排障依赖三大支柱：日志、指标和链路追踪。日志解释单次事件细节，指标发现趋势和告警，trace 把跨服务调用串成链路。23.1 三大支柱            支柱      数据模型      典型问题                  Logging      离散事件文本/JSON      这次请求为什么失败              Metrics      时间序列数值      系统现在是否异常              Tracing      span 树      延迟花在哪里              Profiling      CPU/内存采样      方法级热点      不要只收集而不治理。观测数据越多，成本和噪声越高。23.2 日志结构化字段：timestamplevelserviceinstancetraceIdspanIduserIdpathstatuscostmessage日志规范：  统一 UTF-8；  异常必须打印堆栈；  避免多行普通文本；  不打印密码和 token；  手机号、身份证脱敏；  traceId 必须贯穿；  日志级别可动态调整。JSON 输出可结合日志平台字段提取，减少二次解析成本。23.3 Actuator依赖：&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;&lt;/dependency&gt;配置：management:  endpoints:    web:      exposure:        include: health,info,prometheus,metrics  endpoint:    health:      show-details: never常用端点：            端点      用途                  /actuator/health      健康状态              /actuator/metrics      指标              /actuator/prometheus      抓取              /actuator/env      环境，敏感              /actuator/beans      Bean，敏感              /actuator/configprops      配置，敏感              /actuator/threaddump      线程，敏感      生产默认只暴露最小集合。23.4 Micrometer计数器：@Servicepublic class OrderMetrics {    private final Counter createTotal;    private final Timer createTimer;    public OrderMetrics(MeterRegistry registry) {        this.createTotal = Counter.builder("order_create_total")                .description("Order create count")                .register(registry);        this.createTimer = Timer.builder("order_create_seconds")                .description("Order create latency")                .publishPercentiles(0.5, 0.95, 0.99)                .register(registry);    }}标签使用低基数字段：methodpath templatestatusserviceoutcome不要把 userId、orderId、URL 原文作为 label。23.5 Prometheus 与 Grafana暴露端点：&lt;dependency&gt;    &lt;groupId&gt;io.micrometer&lt;/groupId&gt;    &lt;artifactId&gt;micrometer-registry-prometheus&lt;/artifactId&gt;&lt;/dependency&gt;Prometheus 抓取：scrape_configs:  - job_name: spring-boot    metrics_path: /actuator/prometheus    static_configs:      - targets: ["localhost:8080"]核心指标：            指标      方向                  http_server_requests_seconds      请求延迟              jvm_memory_used_bytes      内存              jvm_gc_pause_seconds      GC              hikaricp_connections_pending      数据库等待              executor_queued      线程池积压              resilience4j_circuitbreaker_state      熔断              process_cpu_usage      CPU      23.6 Micrometer Tracing依赖：&lt;dependency&gt;    &lt;groupId&gt;io.micrometer&lt;/groupId&gt;    &lt;artifactId&gt;micrometer-tracing-bridge-brave&lt;/artifactId&gt;&lt;/dependency&gt;链路结构：traceId = 4bf92f3577b34da6a3ce929d0e0e4736Gateway span  -&gt; Order Controller span     -&gt; Order Service span        -&gt; DB span        -&gt; Inventory Client span传播 Header：traceparent: 00-traceId-spanId-flagsb3: traceId-spanIdx-trace-id自定义 span：@Servicepublic class PricingService {    private final Tracer tracer;    public BigDecimal calculate(Order order) {        Span span = tracer.nextSpan().name("pricing.calculate").start();        try (Tracer.SpanInScope scope = tracer.withSpan(span)) {            return doCalculate(order);        } finally {            span.end();        }    }}23.7 OpenTelemetryOpenTelemetry 提供语言 SDK、Collector 和协议，适合统一日志、指标、trace。App -&gt; OTLP -&gt; Collector -&gt; BackendCollector 能做：  协议转换；  采样；  过滤脱敏；  重试缓冲；  多后端分发。采样策略：            策略      说明                  全采样      成本高，排障方便              按比例采样      常见              尾部采样      根据结果保留慢/错误请求              强制保留      关键接口和 debug 请求      错误和慢请求应优先保留。23.8 告警设计告警模板：名称：严重级别：触发条件：持续时间：影响：排查入口：负责人：预案：核心告警：            指标      条件                  availability      5xx 或不可用              latency P99      超目标              error rate      突增              CPU throttling      持续              memory near limit      高水位              GC P99      超阈值              DB pool pending      大于 0 持续              consumer lag      持续增长              circuit breaker      OPEN      告警必须可执行，否则只适合 dashboard。23.9 日志脱敏错误：log.info("login success, request={}", request);正确：log.info("login success, userId={}, phone={}", request.userId(), mask(request.phone()));常见脱敏：            数据      策略                  手机号      前 3 后 4              身份证      前 1 后 1              银行卡      前 4 后 4              token      只记前缀和长度              密码      完全不记录      对第三方对象输出 toString 前，要确认没有敏感字段。本章小结可观测性要同时建设日志、指标和 trace，并通过 traceId 将三者关联。日志要结构化并脱敏，指标要低基数，trace 要传播到数据库、消息和下游调用。告警应面向可执行动作，避免只堆 dashboard。思考题  日志、指标、trace 分别解决什么问题？  为什么不能用 userId 做指标标签？  如何让错误请求更容易被采样保留？  Actuator 哪些端点不应暴露？  如何设计一条可执行告警？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。事件驱动架构以事件作为服务间协作的重要方式。它降低调用方对下游的直接依赖，支持异步扩展和削峰，但也带来顺序、幂等、回溯和排障复杂度。22.1 事件分类            类型      说明      示例                  事实事件      已发生的事实      OrderCreated              状态变化      状态迁移      OrderCancelled              命令消息      要求执行动作      CreateOrder              通知      弱语义通知      EmailSent              集成事件      跨系统数据同步      ProductUpdated      命名建议使用过去时表达事实：OrderCreatedPaymentSucceededShipmentDelivered命令不是事件。事件发布方不要求消费方返回结果。22.2 事件结构{  "eventId": "01J8Z...",  "eventType": "order.created.v2",  "aggregateId": "10001",  "occurredAt": "2026-08-25T10:00:00Z",  "traceId": "bf2f...",  "producer": "order-service",  "version": 2,  "data": {    "orderId": 10001,    "userId": "u100",    "amount": "99.90"  }}字段要求：  eventId 全局唯一；  schemaVersion 兼容演进；  occurredAt 表达业务时间；  producer 便于溯源；  data 中包含足够业务上下文；  敏感字段脱敏。22.3 Topic 设计按业务域：order-eventspayment-eventsinventory-eventsuser-events按事件类型：order-createdorder-cancelledpayment-succeeded对比：            设计      优点      缺点                  按域      topic 少，消费灵活      消费方过滤多              按事件      权限和订阅清晰      topic 数量多      分区 key：orderIduserIdskuId相同 key 进同一分区，只保证分区内顺序，不保证 topic 全局顺序。22.4 发布可靠性错误方式：@Transactionalpublic void create(Order order) {    repository.save(order);    kafkaTemplate.send("order-events", event); // 事务回滚后消息可能已发出}推荐 Outbox：@Transactionalpublic void create(Order order) {    repository.save(order);    outboxRepository.save(OutboxEvent.from(order));}投递器：select ... from outbox_eventswhere status = 'PENDING'order by idlimit 100for update skip locked发送成功后标记 SENT。失败递增 retry_count，超过阈值进入死信处理。22.5 消费模式Consumer Group  Partition 0 -&gt; Consumer A  Partition 1 -&gt; Consumer B  Partition 2 -&gt; Consumer C并发数：  小于分区数：有消费者空闲；  等于分区数：常规状态；  大于分区数：多余实例不分配分区。消费处理：@KafkaListener(topics = "order-events", groupId = "inventory-service")public void on(OrderCreatedEvent event) {    inventoryService.reserve(event.orderId());}22.6 幂等消费至少一次投递会带来重复消息。处理方式：  消费表记录 eventId；  业务表唯一约束；  状态机限制迁移；  Redis 前置去重，数据库兜底；  事务内写业务和消费记录。示例：@Transactionalpublic void handle(OrderCreatedEvent event) {    if (eventRepository.existsById(event.eventId())) {        return;    }    inventoryService.reserve(event.orderId());    eventRepository.save(new ConsumedEvent(event.eventId()));}Redis 去重不能作为唯一依据，因为缓存可能丢失。22.7 重试与死信错误分类：            错误      处理                  JSON 解析失败      死信，不重试              字段缺失      死信并告警              数据库死锁      短重试              下游超时      指数退避              业务规则拒绝      记录结果，不重试      死信内容：原始消息topic / partition / offsetconsumer group异常栈重试次数traceId时间死信治理：  指标告警；  管理界面；  支持重放；  支持跳过；  人工处理记录；  修复后回放验证。22.8 Schema 演进兼容策略：            变更      是否安全                  新增可选字段      通常安全              新增必填字段      需要默认值              删除字段      视消费者而定              修改字段类型      不安全              改语义      不安全      实践：  使用 schema registry；  新旧消费者并行验证；  先发布兼容 schema，再发代码；  事件类型带版本；  必要时新建 topic；  保留回放工具。22.9 事件溯源事件溯源把事件作为事实来源：OrderCreatedOrderPaidOrderShippedOrderCancelled重放事件得到当前状态：public Order replay(List&lt;OrderEvent&gt; events) {    Order order = null;    for (OrderEvent event : events) {        order = event.apply(order);    }    return order;}优点：审计、回放、时间旅行分析。代价：  查询需要投影；  事件不可修改；  schema 演进复杂；  一致性构建成本高；  团队理解成本高。不是所有事件驱动系统都必须事件溯源。22.10 监控治理指标：producer_send_totalproducer_error_totaloutbox_pendingoutbox_send_latencyconsumer_lagconsumer_latencyconsumer_retry_totaldead_letter_totalevent_age告警：  outbox 积压；  lag 持续增长；  死信增加；  消费耗时上升；  schema 不兼容；  事件年龄过高。治理要求：  每个 topic 有 owner；  每个 consumer group 有 owner；  事件契约有文档；  破坏性变更走流程；  保留期和容量规划明确；  支持按 aggregateId 回放。本章小结事件驱动架构通过事件解耦生产者和消费者，适合异步协作、数据同步和削峰场景。生产侧应使用 Outbox 保证业务与事件一致；消费侧必须幂等、有限重试和死信治理；契约必须版本化演进。事件不是银弹，强实时查询和复杂返回值场景仍适合 API 调用。思考题  事件和命令有什么区别？  为什么事务内直接发送消息不可靠？  Kafka 分区 key 如何影响顺序？  消费端有哪些幂等方案？  事件溯源适合什么业务？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。当一次业务操作跨多个服务或多个数据库时，本地事务无法保证全局原子性。分布式事务的核心不是“永远不出错”，而是定义清晰的中间状态、补偿路径和一致性边界。21.1 问题模型下单：1. 创建订单2. 预占库存3. 调用支付4. 发送履约指令可能失败：            阶段      失败      处理                  创建订单      本地失败      直接返回              预占库存      不足      取消订单              支付      超时      查询支付结果              履约      失败      退款或人工处理      关键问题：支付超时不知道成功还是失败时，不能盲目重试支付，必须先查证。21.2 BASE 与最终一致BASE：Basically AvailableSoft StateEventually Consistent互联网业务常接受短暂中间态：订单状态：CREATED库存状态：RESERVED支付状态：PAYING要求：  状态机合法；  幂等；  可重试；  可补偿；  有对账；  有超时处理。最终一致不是不管，而是自动化补偿和可审计。21.3 2PC / XA流程：Coordinator  -&gt; prepare 所有参与者  -&gt; 全部 yes 则 commit  -&gt; 任一 no 则 rollback优点：强一致。缺点：  锁资源时间长；  协调者单点；  阻塞风险；  吞吐下降；  运维复杂。适合金额账务、强一致库内跨库等场景，不适合高并发长链路。21.4 TCC三个阶段：            阶段      含义      示例                  Try      预留资源      冻结库存              Confirm      确认提交      扣减冻结库存              Cancel      取消释放      解冻库存      表设计：stock  available  frozenTry：update stockset available = available - 10, frozen = frozen + 10where sku_id = ? and available &gt;= 10;Confirm：update stockset frozen = frozen - 10where sku_id = ?;Cancel：update stockset available = available + 10, frozen = frozen - 10where sku_id = ?;要求：  Try 先做资源检查；  Confirm/Cancel 幂等；  允许空回滚；  防悬挂；  事务日志持久化。21.5 SagaSaga 将长事务拆成一组本地事务，每步失败时执行逆向补偿。正向：CreateOrder -&gt; ReserveStock -&gt; ChargePayment -&gt; CreateShipment补偿：CancelShipment -&gt; RefundPayment -&gt; ReleaseStock -&gt; CancelOrder两种编排：            模式      特点                  编排式      中央协调器维护状态              协同式      服务监听事件并发布下一步      注意：  有些操作不可自动补偿，如已发货；  Saga 中间状态对用户可见；  需要并发控制和锁；  需要状态表和重试任务；  补偿也必须幂等。21.6 OutboxOutbox 解决“本地事务成功但事件发送失败”的问题。本地事务  写业务表  写 outbox_events异步投递  扫描 outbox  发送消息  更新状态优点：  保证事件不因发送失败丢失；  事件顺序可按聚合 ID 保证；  易重放；  与本地事务一致。配合 CDC（Debezium 等）可以减少扫描和轮询压力。21.7 幂等设计唯一业务 ID：orderId + operationeventIdpaymentFlowId数据库唯一约束：create table payment_instructions (  payment_id varchar(64) primary key,  order_id varchar(64) not null,  amount decimal(18,2) not null,  status varchar(20) not null,  created_at timestamp not null);状态机：INIT -&gt; PAYING -&gt; SUCCESS            \-&gt; FAILED            \-&gt; CLOSED只允许合法迁移：update payment_instructionsset status = 'SUCCESS'where payment_id = ? and status = 'PAYING';影响行数为 0 表示状态已迁移或指令不存在。21.8 对账自动补偿不能覆盖所有异常，必须对账。数据源：订单库支付渠道账单库存流水账户流水消息日志对账流程：1. 拉取日切账单2. 按业务 ID 关联3. 找金额、状态、时间差异4. 自动修复可确定差异5. 人工处理例外6. 记录处理结果指标：recon_totalrecon_diff_totalrecon_auto_fixed_totalrecon_manual_totalrecon_max_lag21.9 事务消息RocketMQ 支持事务消息：发送 half 消息  -&gt; 执行本地事务  -&gt; commit / rollback  -&gt; broker 回查本地事务状态适用：  本地事务与消息发送需要一致性；  消费方可接受最终一致；  有回查接口；  消费方幂等。与 Outbox 的区别：事务消息依赖消息中间件能力；Outbox 使用数据库表作为可靠缓冲，更通用但需要投递任务或 CDC。21.10 方案选择            场景      建议                  单服务多表      本地事务              服务间事件通知      Outbox + Kafka              库存预占      TCC 或状态机补偿              长流程订单      Saga              强一致资金入账      XA / TCC / 账务系统设计              已发货回滚      逆向流程，不是简单回滚      选择问题：  是否允许中间态；  是否允许短暂不一致；  失败概率；  补偿成本；  吞吐要求；  审计要求；  团队运维能力。本章小结分布式事务应以业务状态机为核心，结合本地事务、幂等、Outbox、Saga、TCC、事务消息和对账形成完整方案。强一致方案成本高，最终一致方案必须有补偿、重试、监控和人工处理边界。不要把分布式事务当成一个注解，而是当成一套业务流程可靠性设计。思考题  TCC 如何处理空回滚和悬挂？  Saga 的补偿失败怎么办？  Outbox 解决什么问题？  支付超时为什么必须查证后处理？  如何设计订单与支付的对账？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。分布式系统中，故障不可避免。弹性的目标是让局部故障不演变成全局雪崩，并在资源不足时保护核心链路。熔断、限流、隔离和降级是四种互补手段。20.1 故障传播下游变慢  -&gt; 调用线程阻塞  -&gt; 线程池耗尽  -&gt; 上游无法处理其他请求  -&gt; 更多服务级联失败保护顺序：超时 -&gt; 限流 -&gt; 并发隔离 -&gt; 熔断 -&gt; 降级 -&gt; 恢复没有超时的重试和熔断都可能加剧故障。20.2 Resilience4j依赖：&lt;dependency&gt;    &lt;groupId&gt;io.github.resilience4j&lt;/groupId&gt;    &lt;artifactId&gt;resilience4j-spring-boot3&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;    &lt;artifactId&gt;spring-cloud-starter-circuitbreaker-resilience4j&lt;/artifactId&gt;&lt;/dependency&gt;配置：resilience4j:  circuitbreaker:    instances:      inventory:        sliding-window-size: 100        minimum-number-of-calls: 20        failure-rate-threshold: 50        wait-duration-in-open-state: 10s        permitted-number-of-calls-in-half-open-state: 5使用：@Servicepublic class InventoryClient {    @CircuitBreaker(name = "inventory", fallbackMethod = "fallback")    public InventoryView get(Long skuId) {        return restClient.get().uri("/api/inventories/{id}", skuId)                .retrieve()                .body(InventoryView.class);    }    private InventoryView fallback(Long skuId, Throwable cause) {        return InventoryView.unknown(skuId);    }}20.3 熔断器状态CLOSED  -&gt; 失败率达到阈值OPEN  -&gt; 等待冷却HALF_OPEN  -&gt; 放少量探测请求  -&gt; 成功恢复 CLOSED  -&gt; 失败回到 OPEN参数：            参数      说明                  sliding window      统计窗口              minimum calls      最小调用数              failure rate      失败率阈值              slow call rate      慢调用比例              wait duration      打开时长              half-open calls      探测请求数      不要只按失败率熔断，慢调用往往是雪崩前兆。20.4 限流resilience4j:  ratelimiter:    instances:      createOrder:        limit-for-period: 100        limit-refresh-period: 1s        timeout-duration: 0@RateLimiter(name = "createOrder")public Order create(CreateOrderCommand command) {    return orderService.create(command);}限流位置：            位置      特点                  网关      全局和用户级              应用      业务细粒度              数据库      保护连接和 SQL              下游客户端      保护依赖      被限流后可返回：  429；  排队等待；  降级结果；  优先级策略；  异步任务。20.5 并发隔离信号量隔离：@Bulkhead(name = "inventory", type = Bulkhead.Type.SEMAPHORE)public InventoryView get(Long id) {}线程池隔离：inventory-poolpay-pooluser-pool对比：            方式      优点      缺点                  信号量      轻量、无线程切换      不能设置异步超时              线程池      隔离强、可超时      线程成本高      阻塞 IO 常用线程池隔离；Reactive 或轻量保护可用信号量。20.6 降级设计降级策略：            场景      策略                  推荐服务不可用      返回默认推荐              评论服务不可用      隐藏评论区              库存查询失败      显示“暂不可购买”              优惠券失败      不优惠并提示              支付失败      引导重试              搜索失败      返回历史热门      设计原则：  核心交易优先；  降级结果业务可接受；  数据不能错账；  用户明确感知；  降级有开关和审计；  恢复后自动或人工切回。20.7 重试与熔断组合顺序建议：RateLimiter  -&gt; CircuitBreaker     -&gt; Retry        -&gt; TimeLimiter           -&gt; 实际调用常见错误：  熔断已打开仍重试；  非幂等 POST 重试；  重试无预算；  fallback 吞掉异常导致无监控；  fallback 又调用不可用下游；  所有服务共享一个熔断器。fallback 必须记录：private InventoryView fallback(Long id, Throwable cause) {    log.warn("inventory fallback, id={}, type={}", id, cause.getClass().getSimpleName());    return InventoryView.unknown(id);}20.8 Sentinel 选择Sentinel 提供流量控制、熔断、热点限流、系统自适应保护和控制台。适合需要集中规则管理和实时调整的场景。依赖：&lt;dependency&gt;    &lt;groupId&gt;com.alibaba.cloud&lt;/groupId&gt;    &lt;artifactId&gt;spring-cloud-starter-alibaba-sentinel&lt;/artifactId&gt;&lt;/dependency&gt;配置：spring:  cloud:    sentinel:      transport:        dashboard: sentinel.example.com:8080与 Resilience4j 对比：            维度      Resilience4j      Sentinel                  风格      轻量库      库 + 控制台生态              规则      配置文件和代码      支持动态规则              热点      需要自行扩展      内置支持              适用      Spring Cloud 标准      Alibaba 生态常见      按团队平台选择，不叠加两套复杂体系。20.9 指标与演练指标：circuit_statecircuit_failure_ratecircuit_slow_call_ratecircuit_calls_totalratelimit_available_permitsratelimit_rejected_totalbulkhead_availablefallback_totaldownstream_latency告警：  熔断打开；  限流拒绝突增；  fallback 突增；  线程池排队；  下游 P99 超标。演练场景：  下游延迟 5 秒；  下游返回 500；  数据库连接耗尽；  缓存失效；  实例减半；  网络抖动。验证服务是否按预期降级，而不是直接线程池耗尽。本章小结弹性治理通过超时、限流、隔离、熔断和降级防止故障扩散。Resilience4j 适合轻量集成，Sentinel 适合需要控制台和动态规则的生态。降级必须符合业务语义，重试必须幂等且有预算，所有保护动作都要有指标、日志和演练验证。思考题  为什么说没有超时的重试是危险的？  熔断 OPEN 状态的作用是什么？  信号量隔离和线程池隔离如何选择？  fallback 中为什么必须记录日志？  如何设计一次雪崩演练？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。网关是外部流量进入内部系统的统一边界，负责路由、认证、限流、熔断、协议转换、观测和安全策略。它应该保持轻量稳定，避免承载业务逻辑。19.1 网关职责Client  -&gt; Gateway     -&gt; 路由     -&gt; 认证     -&gt; 限流     -&gt; 日志     -&gt; 负载均衡     -&gt; Order Service            职责      说明                  路由      按路径、Host、Header 转发              认证      验证 Token、签名              授权      判断可访问资源              限流      保护后端              熔断      阻止故障扩散              观测      记录访问日志和指标              安全      TLS、WAF、CORS              发布      灰度、蓝绿、金丝雀      不建议在网关写业务规则，例如复杂促销计算。网关逻辑越重，变更和故障影响越大。19.2 Spring Cloud Gateway依赖：&lt;dependency&gt;    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;    &lt;artifactId&gt;spring-cloud-starter-gateway-server-webflux&lt;/artifactId&gt;&lt;/dependency&gt;配置：server:  port: 8080spring:  cloud:    gateway:      server:        webflux:          routes:            - id: order-route              uri: lb://order-service              predicates:                - Path=/api/orders/**              filters:                - name: Retry                  args:                    retries: 2                    statuses: BAD_GATEWAY                - StripPrefix=0Spring Cloud Gateway 基于 Reactive 技术栈，不适合直接使用阻塞 API。版本较新时配置命名可能有调整，以当前文档为准。19.3 路由谓词常用谓词：            谓词      说明                  Path      路径匹配              Method      HTTP 方法              Header      请求头              Query      查询参数              Host      域名              Weight      按权重路由              Cookie      Cookie 匹配      示例：routes:  - id: canary    uri: lb://order-service-v2    predicates:      - Path=/api/orders/**      - Header=X-Canary, true路由顺序要明确，避免宽泛规则先匹配。19.4 过滤器全局过滤器：@Componentpublic class TraceIdFilter implements GlobalFilter, Ordered {    @Override    public Mono&lt;Void&gt; filter(ServerWebExchange exchange, GatewayFilterChain chain) {        String traceId = Optional.ofNullable(exchange.getRequest().getHeaders().getFirst("X-Trace-Id"))                .orElseGet(() -&gt; UUID.randomUUID().toString());        exchange = exchange.mutate()                .request(builder -&gt; builder.header("X-Trace-Id", traceId))                .build();        exchange.getResponse().getHeaders().set("X-Trace-Id", traceId);        return chain.filter(exchange);    }    @Override    public int getOrder() {        return -100;    }}过滤器顺序：TraceId  -&gt; 安全  -&gt; 限流  -&gt; 认证  -&gt; 路由修改  -&gt; 转发  -&gt; 响应处理19.5 认证与授权常见模式：            模式      做法                  JWT      网关验签，传递用户声明              OAuth2      网关或资源服务器校验 token              Session      网关透传 Cookie              API Key      识别调用方              mTLS      服务间身份认证      认证后传递：X-User-IdX-User-RolesX-Client-IdX-Trace-Id必须注意：  这些 Header 只能由网关注入或覆盖；  内部服务不能信任外部传入的伪造身份头；  敏感 token 不应原样写入日志；  授权规则要与资源绑定。19.6 限流限流维度：  全局；  服务；  路径；  用户；  IP；  租户；  API Key。算法：            算法      特点                  固定窗口      简单，边界突刺              滑动窗口      平滑，内存稍高              令牌桶      允许突发              漏桶      平滑输出              分布式 Redis + Lua      多实例一致      响应：429 Too Many RequestsRetry-After: 1X-RateLimit-Limit: 100X-RateLimit-Remaining: 0限流不是只给 429，还要考虑排队、优先级和降级。19.7 超时与重试配置：spring:  cloud:    gateway:      httpclient:        connect-timeout: 1000        response-timeout: 5s超时链路：客户端超时  &gt;= 网关总超时     &gt; 后端服务超时网关重试必须谨慎：  默认只重试幂等请求；  POST 下单不应盲目重试；  有幂等键才可重试；  限制重试次数；  设置熔断保护。19.8 灰度发布常见策略：            策略      做法                  Header 灰度      指定用户或内部流量              权重灰度      5% 流量到新版本              租户灰度      指定客户先体验              地域灰度      某机房先发布              Cookie 灰度      白名单用户      示例：routes:  - id: order-v1    uri: lb://order-service-v1    predicates:      - Path=/api/orders/**      - Weight=order-group, 95  - id: order-v2    uri: lb://order-service-v2    predicates:      - Path=/api/orders/**      - Weight=order-group, 5必须配套：  版本指标；  快速回滚；  一致性校验；  用户粘性；  业务观察窗口。19.9 可观测性访问日志字段：timeremote_ipmethodpathstatusbytesrequest_timeupstream_serviceupstream_hostupstream_statustrace_iduser_idclient_id指标：gateway_request_totalgateway_request_secondsgateway_status_totalgateway_timeout_totalgateway_limit_totalgateway_upstream_secondsgateway_active_connections告警：  5xx 比例；  P99 延迟；  429 增长；  upstream timeout；  路由无健康实例；  网关内存和连接数。19.10 高可用部署：DNS / SLB  -&gt; Gateway 多实例     -&gt; 服务多实例要点：  网关无状态；  配置热更新可回滚；  后端健康检查；  网关 CPU 和内存充足；  避免阻塞调用；  限制请求体大小；  防慢连接攻击；  TLS 证书管理；  压测长连接和并发；  定期故障演练。本章小结网关是流量、安全、观测和发布策略的统一边界。Spring Cloud Gateway 提供路由谓词和过滤器模型，生产中要重点治理认证头、超时、限流、重试、灰度和可观测性。网关应保持轻量、无状态和高可用，业务规则应下沉到服务内部。思考题  为什么网关不适合承载复杂业务逻辑？  网关认证后如何安全传递用户身份？  限流维度如何选择？  哪些请求可以在网关重试？  灰度发布需要哪些配套能力？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。服务调用负责把“逻辑服务名”变成一次具体请求。它包括客户端发现、负载均衡、超时、重试、熔断、隔离、重试预算和可观测性。调用链路设计不当，会放大故障。18.1 调用方式            方式      契约      特点                  RestTemplate / RestClient      URL / HTTP      简单直接              WebClient      HTTP / Reactive      非阻塞              OpenFeign      接口注解      声明式，常见              gRPC      Protobuf      高性能、强契约              Dubbo      接口      RPC 生态完整      选择依据：  是否需要强契约；  是否跨语言；  是否浏览器可访问；  性能要求；  团队技术栈；  现有治理能力。18.2 Spring Cloud LoadBalancer依赖：&lt;dependency&gt;    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;    &lt;artifactId&gt;spring-cloud-starter-loadbalancer&lt;/artifactId&gt;&lt;/dependency&gt;RestClient 使用服务名：@Configurationpublic class HttpClientConfig {    @Bean    @LoadBalanced    RestClient.Builder restClientBuilder() {        return RestClient.builder();    }}调用：@Servicepublic class InventoryClient {    private final RestClient restClient;    public InventoryClient(RestClient.Builder builder) {        this.restClient = builder.baseUrl("http://inventory-service").build();    }    public InventoryView get(Long skuId) {        return restClient.get()                .uri("/api/inventories/{id}", skuId)                .retrieve()                .body(InventoryView.class);    }}http://inventory-service 会由 LoadBalancer 解析成具体实例。18.3 OpenFeign依赖：&lt;dependency&gt;    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;    &lt;artifactId&gt;spring-cloud-starter-openfeign&lt;/artifactId&gt;&lt;/dependency&gt;启用：@EnableFeignClients@SpringBootApplicationpublic class OrderApplication {}客户端：@FeignClient(name = "inventory-service", path = "/api/inventories")public interface InventoryFeignClient {    @GetMapping("/{id}")    InventoryView get(@PathVariable("id") Long id);    @PutMapping("/{id}/reserve")    void reserve(@PathVariable("id") Long id, @RequestBody ReserveRequest request);}Feign 将接口代理成 HTTP 调用，并集成注册中心、负载均衡和编解码器。18.4 负载均衡策略常见策略：            策略      特点                  Round Robin      轮询，默认常见              Random      随机              Weighted      按权重              Zone Aware      同机房优先              Least Requests      少请求优先              Consistent Hash      相同 key 到相同实例      选择：  普通无状态服务：轮询；  机器配置差异大：权重；  多机房：同机房优先；  本地缓存：一致性哈希；  请求耗时差异大：最少请求。负载均衡不能解决所有热点，业务 key 设计仍关键。18.5 超时必须为每层设置超时：spring:  cloud:    openfeign:      client:        config:          inventory-service:            connect-timeout: 500            read-timeout: 2000超时层次：网关超时  &gt; 服务间调用超时    &gt; 数据库 / 缓存超时原则：  连接超时通常小于读超时；  下游超时不能超过上游允许时间；  不同接口不同超时；  超时后记录目标实例；  超时配置可观测。没有超时等于把下游故障放大成线程池耗尽。18.6 重试可重试条件：  请求幂等；  错误类型可恢复；  下游仍有余量；  重试次数有限；  有退避；  有重试预算。示例：GET /products/{id} 可重试POST /orders 不应盲目重试，除非有幂等键PUT /configs/{id} 全量更新可按契约重试退避：100ms200ms400msjitter重试风暴：上游 100 请求  -&gt; 每个重试 3 次  -&gt; 下游瞬间 400 请求控制方式：  每实例重试上限；  重试预算比例；  熔断优先；  只重试连接失败或明确可重试状态；  限制并发。18.7 线程池隔离调用不同下游使用独立线程池：Order Service  |-- pay-pool  |-- inventory-pool  |-- user-pool好处：  一个下游慢不会耗尽全部线程；  故障范围可控；  可单独配置队列和超时；  便于监控。缺点：  线程数增加；  配置复杂；  需要监控每个池；  可能掩盖容量问题。关键指标：activequeue sizerejectedtask latencydownstream latency18.8 熔断与降级熔断器状态：CLOSED  -&gt; 失败率超过阈值OPEN  -&gt; 冷却时间后HALF_OPEN  -&gt; 放少量请求探测  -&gt; 成功恢复 CLOSED / 失败回到 OPEN降级方式：  返回缓存；  返回默认值；  功能关闭；  异步补偿；  提示用户稍后重试。熔断不是让错误消失，而是防止故障扩散。核心交易链路的降级必须符合业务规则。18.9 Hedging 与单飞Hedging：同时向多个实例发请求，取最先成功结果。优点：降低尾延迟。代价：  下游流量倍增；  副作用请求需幂等；  浪费资源；  排查复杂。适合：  只读请求；  P999 极敏感；  下游余量充足；  有严格预算。singleflight：同一 key 并发请求只放一个回源，其他等待结果。适合热点缓存重建。18.10 可观测性每次调用记录：traceIdcaller servicetarget servicetarget instancemethod / pathstatuslatencytimeoutretry counterror type指标：client_request_secondsclient_request_totalclient_timeout_totalclient_retry_totalclient_circuit_openpool_activepool_queuepool_rejected告警：  错误率突增；  P99 超阈值；  超时增加；  熔断打开；  线程池拒绝；  某实例失败率异常。本章小结服务调用不只是发 HTTP 请求，而是服务发现、负载均衡、超时、重试、熔断、隔离和观测的组合。生产调用方必须明确幂等性、重试预算和降级语义；每层超时要逐级收敛；指标要能定位到目标实例，否则故障排查仍靠猜。思考题  为什么每个下游调用必须有超时？  哪些请求可以重试？  线程池隔离有什么代价？  Hedging 适合什么场景？  如何定位调用到了哪个坏实例？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。配置中心把配置从应用包和本机环境中抽离，提供集中管理、多环境隔离、动态刷新、审计和回滚能力。它是微服务治理的基础组件之一。17.1 为什么需要配置中心没有配置中心时：  每台机器维护配置文件；  发布后才能改配置；  环境差异容易出错；  配置变更无审计；  密钥散落；  多服务开关难以统一。集中配置模型：配置中心  |-- dev / test / prod  |-- order-service.yaml  |-- inventory-service.yaml  +-- shared-common.yaml应用启动拉取配置运行时订阅变更17.2 配置分类            类型      示例      更新方式                  静态基础设施      数据库 URL、端口      重启              环境配置      日志级别、线程池      可动态              业务开关      新功能灰度      动态              限流阈值      QPS、并发数      动态              密钥      密码、Token      密钥系统              大对象      路由规则、词典      版本化推送      不是所有配置都适合动态刷新。连接类、线程池核心参数、复杂拓扑变更通常应重启验证。17.3 常见方案            方案      特点                  Spring Cloud Config      Git 后端、版本化              Nacos      配置与服务发现一体              Consul KV      与 Consul 生态结合              Apollo      统一管控、审批、发布模型强              Kubernetes ConfigMap      平台原生              Vault      密钥专用      选择考虑：  是否需要审批发布；  是否需要灰度；  多数据中心支持；  权限模型；  客户端复杂度；  与现有平台整合；  审计合规。17.4 Spring Cloud Config服务端：server:  port: 8888spring:  cloud:    config:      server:        git:          uri: https://git.example.com/config/order-config.git          search-paths: '{application}'          default-label: main客户端：spring:  application:    name: order-service  cloud:    config:      uri: https://config.example.com      label: main      fail-fast: true配置仓库文件：order-service.ymlorder-service-prod.ymlapplication.ymlSpring Cloud Config 的配置通常通过 Git 提交保留版本历史，也可通过消息总线触发刷新。17.5 Nacos Config依赖：&lt;dependency&gt;    &lt;groupId&gt;com.alibaba.cloud&lt;/groupId&gt;    &lt;artifactId&gt;spring-cloud-starter-alibaba-nacos-config&lt;/artifactId&gt;&lt;/dependency&gt;配置：spring:  application:    name: order-service  cloud:    nacos:      config:        server-addr: nacos.example.com:8848        namespace: prod        group: TRADE_GROUP        file-extension: yamlNacos 支持多 dataId 优先级、扩展配置和共享配置。不同版本行为有差异，应统一客户端版本并验证加载顺序。17.6 动态刷新启用：@RefreshScope@Componentpublic class RiskProperties {    @Value("${risk.enabled:false}")    private boolean enabled;}触发刷新：curl -X POST http://localhost:8080/actuator/refresh刷新流程：配置中心变更  -&gt; 客户端感知  -&gt; Environment 重新绑定  -&gt; @RefreshScope Bean 销毁重建  -&gt; 下次访问使用新值注意：  singleton Bean 中的字段不会自动重建；  @ConfigurationProperties 通常更适合动态更新；  RefreshScope Bean 中不应保存重要状态；  刷新可能触发 Bean 重建开销；  需要记录刷新事件。更稳妥的业务开关可以使用内存快照：@Componentpublic class FeatureFlags {    private final AtomicReference&lt;Map&lt;String, Boolean&gt;&gt; flags =            new AtomicReference&lt;&gt;(Map.of());    public void update(Map&lt;String, Boolean&gt; next) {        flags.set(Map.copyOf(next));    }    public boolean enabled(String key) {        return flags.get().getOrDefault(key, false);    }}17.7 配置优先级常见顺序：命令行环境变量配置中心本地 application-{profile}.ymlapplication.yml默认值真实优先级由 PropertySource 顺序决定，配置中心客户端可能在启动早期插入。排查时应查看 Environment：/actuator/env团队规范：  一个配置只有一个来源；  环境差异放环境维度；  公共默认值放共享配置；  特殊覆盖显式说明；  不重复配置同一个键。17.8 灰度发布配置灰度策略：按实例灰度：实例 A/B 使用 beta 配置按集群灰度：先测试集群，再全量按用户灰度：配置平台 + 应用规则要求：  配置带版本号；  可快速回滚；  观察指标；  限制灰度范围；  记录操作人；  支持一键全量或回退。17.9 配置安全风险：  配置中心账号权限过宽；  明文密码；  env 端点暴露；  Git 仓库泄露；  变更无审计；  谁都可以修改限流阈值。建议：  按命名空间、Group、应用授权；  密钥使用 Vault、KMS 或云 Secret；  配置加密传输和存储；  变更审批和审计；  只读账号给只读场景；  生产修改双人确认；  日志脱敏；  定期轮转密钥。17.10 配置治理配置模板：app:  feature:    new-order-flow: false  ratelimit:    create-order-qps: 100  downstream:    pay:      timeout: 3s      retry: 2上线检查：  必需键校验；  默认值安全；  环境隔离；  版本记录；  回滚脚本；  监控指标；  变更通知；  契约文档。本章小结配置中心提供集中管理、环境隔离、动态刷新、审计和灰度能力。配置应分为静态、动态和密钥三类，动态配置要有明确刷新语义和安全边界。生产变更必须具备版本、审计、灰度和回滚机制，密钥应交给专门密钥系统管理。思考题  哪些配置不适合动态刷新？  @RefreshScope 的副作用是什么？  如何确认一个配置最终来自哪里？  配置灰度和服务发布灰度有什么差异？  配置中心高可用如何设计？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。注册中心解决服务实例动态寻址问题。微服务实例会频繁发布、扩容、重启和故障，调用方不能依赖写死的 IP，而应从注册中心获取健康实例列表。16.1 核心模型Provider 启动  -&gt; 注册服务名、IP、端口、元数据  -&gt; 定时续约Consumer 调用  -&gt; 按服务名拉取实例  -&gt; 本地缓存列表  -&gt; 负载均衡选择实例Provider 下线  -&gt; 注销  或健康检查失败  -&gt; 注册中心标记不可用核心概念：            概念      说明                  Service      逻辑服务名              Instance      IP、端口、元数据              Namespace      环境或租户隔离              Group      业务分组              Health Check      存活判断              Subscribe      消费者订阅变更      16.2 常见实现            注册中心      特点                  Nacos      服务发现与配置中心一体，国内常见              Consul      多数据中心、健康检查生态              Eureka      AP 风格，Netflix 生态，新项目使用减少              ZooKeeper      CP 风格，常用于中间件              Kubernetes Service      平台原生服务发现      选择考虑：  团队已有基础设施；  一致性与可用性取舍；  健康检查能力；  多环境隔离；  权限体系；  运维成本；  云平台集成。16.3 CAP 取舍            模式      说明                  AP      优先可用性，允许短暂旧列表              CP      优先一致性，主从切换时可能拒绝服务      服务发现通常更偏向 AP：调用方宁可使用短暂过期的健康列表，也不希望在注册中心抖动时完全无法调用。但 AP 不等于不要一致，实例异常仍需要通过健康检查、连接失败统计和熔断快速剔除。16.4 Spring Cloud DiscoveryNacos 依赖：&lt;dependency&gt;    &lt;groupId&gt;com.alibaba.cloud&lt;/groupId&gt;    &lt;artifactId&gt;spring-cloud-starter-alibaba-nacos-discovery&lt;/artifactId&gt;&lt;/dependency&gt;配置：spring:  application:    name: order-service  cloud:    nacos:      discovery:        server-addr: nacos.example.com:8848        namespace: prod        group: TRADE_GROUP服务提供方无需特殊注解，Spring Boot 应用会自动注册。服务消费方可通过 DiscoveryClient 查询：@Servicepublic class InstanceService {    private final DiscoveryClient discoveryClient;    public List&lt;ServiceInstance&gt; instances() {        return discoveryClient.getInstances("inventory-service");    }}16.5 健康检查常见模式：            模式      说明                  心跳续约      Provider 定时向注册中心上报              注册中心主动探测      Consul 健康检查              Kubernetes 探针      liveness/readiness              应用自检      /actuator/health      推荐同时区分：            探针      含义                  liveness      进程是否需要重启              readiness      是否接收流量              startup      是否完成启动      发布时应先改 readiness 为 not ready，等流量排空再停进程。16.6 实例状态实例元数据建议：spring:  cloud:    nacos:      discovery:        metadata:          version: "1.8.0"          zone: cn-east-1a          weight: "100"可用于灰度、同机房优先、金丝雀发布。更通用的做法是通过部署平台控制 readiness，而不是在每个应用手写上下线接口。16.7 服务列表缓存消费者通常会本地缓存服务列表，并订阅推送变更。好处：  注册中心故障时仍可调用；  减少查询压力；  降低延迟。风险：  短暂旧实例；  客户端负载均衡状态不一致；  推送风暴；  注册中心恢复后需要同步。调用方仍必须有连接失败重试和熔断，不能假设列表永远正确。16.8 多环境隔离常见隔离维度：Namespace：dev / test / prodGroup：trade / search / paymentCluster：public / internal严禁测试服务注册到生产 namespace。配置建议：  生产地址通过环境变量注入；  namespace 与部署平台绑定；  注册中心账号按环境隔离；  权限最小化；  上线前校验服务列表。16.9 注册中心高可用部署：nacos / consul cluster  3 或更多节点  前端负载均衡  数据持久化  监控与备份故障演练：  停一个注册中心节点；  网络分区；  注册中心全部不可用；  恢复后观察状态同步。验收标准：  服务注册不失败；  消费者可使用本地缓存；  实例恢复后自动同步；  无重复注册或脏实例；  监控告警及时。16.10 常见问题            问题      排查                  服务找不到      namespace、group、服务名              调到下线实例      健康检查、readiness、缓存              重复注册      网卡多 IP、实例 ID              注册慢      心跳、网络、启动就绪顺序              生产看到测试实例      环境隔离失效              实例列表抖动      GC、健康检查超时、探针过严      多网卡场景应显式指定 IP：spring.cloud.nacos.discovery.ip=10.0.10.11本章小结注册中心提供动态服务发现能力，核心是服务名、实例、健康检查和订阅推送。选型要考虑 CAP 取舍、环境隔离、健康检查和运维成本。消费者必须本地缓存列表并配合负载均衡、失败重试与熔断，发布时要正确处理 readiness，避免调用下线实例。思考题  服务发现为什么通常偏向 AP？  liveness 和 readiness 有什么区别？  消费者为什么需要缓存服务列表？  如何避免测试服务注册到生产？  调用到已下线实例时如何排查？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。微服务拆分不是把代码按目录切开，而是重新划分业务能力、数据所有权、团队协作和发布边界。拆得好，系统独立演进；拆得差，只是把单体复杂度变成分布式复杂度。15.1 单体与微服务单体：Client -&gt; Monolith -&gt; One Database优点：  开发简单；  事务一致；  调试方便；  部署少；  初期成本低。痛点：  发布互相阻塞；  局部故障影响全局；  团队协作冲突；  技术栈统一约束；  数据库成为瓶颈。微服务：Client  -&gt; Gateway     -&gt; Order Service -&gt; Order DB     -&gt; Product Service -&gt; Product DB     -&gt; Inventory Service -&gt; Inventory DB微服务解决的是组织和演进问题，不是性能银弹。15.2 拆分依据常用方法：            方法      核心问题                  DDD 限界上下文      业务语言和边界在哪里              数据所有权      谁拥有哪些表和事实              变更频率      哪些能力变化快              团队拓扑      团队如何协作              可伸缩性      哪些模块资源差异大              故障隔离      哪些能力需要独立降级      优先级：业务边界 &gt; 数据边界 &gt; 团队边界 &gt; 技术边界不要按技术层拆成 Controller 服务、Service 服务、DAO 服务，这会放大远程调用。15.3 限界上下文电商示例：Catalog Context：商品、类目、搜索词Inventory Context：库存、预占、入库Order Context：订单、状态、履约指令Payment Context：支付单、渠道、退款Logistics Context：运单、配送、轨迹同一个词在不同上下文有不同含义：            词语      Catalog      Order      Logistics                  Product      商品资料      下单时快照      包裹内容              User      浏览者      买家      收件人      跨上下文通信应使用明确契约和防腐层，而不是共享实体类。15.4 服务粒度过粗：trade-service 包含订单、支付、库存、履约过细：order-query-serviceorder-command-serviceorder-validation-serviceorder-price-service粒度判断：            信号      说明                  两个服务总是同时发布      边界可能错              一个服务需要频繁查另一个数据库      数据边界可能错              一个接口需要 5 个以上服务协作      可能过细              小团队维护 20 个服务      成本过高              数据库表仍跨服务强关联      拆分不完整      建议从“粗粒度微服务”开始，边界稳定后再细化。15.5 数据拆分阶段演进：阶段 1：单库单 schema阶段 2：单实例多 schema阶段 3：服务独立数据库阶段 4：按业务进一步分库分表原则：  每个服务拥有自己的数据；  不允许跨服务直连表；  跨服务查询用 API 或事件同步副本；  报表使用数仓或搜索索引；  共享只读主数据要明确所有者。错误示例：Order Service -&gt; select * from inventory_db.stock正确方向：Order Service -&gt; Inventory Service APIOrder Service &lt;- InventoryChangedEvent15.6 服务通信同步调用：Order -&gt; Inventory -&gt; Product优点：实时、简单。问题：  级联失败；  延迟叠加；  可用性下降；  强耦合；  事务复杂。异步事件：Order -&gt; OrderCreatedEvent -&gt; KafkaInventory 消费事件Notification 消费事件优点：解耦、削峰、扩展消费者。问题：  最终一致；  幂等复杂；  排查链路长；  事件治理要求高。选择：            场景      建议                  下单前检查库存      同步或预检              下单后扣库存      事件或 Saga              发送通知      事件              查询实时价格      同步缓存              生成报表      异步任务      15.7 分布式事务下单流程：1. 创建订单2. 预占库存3. 支付4. 确认订单失败处理：            阶段失败      补偿                  库存预占失败      取消订单              支付失败      释放库存              支付成功但订单失败      退款              履约失败      人工或自动补偿      方案：            方案      说明                  Saga      每步有对应补偿              TCC      Try、Confirm、Cancel              Outbox      保证事件可靠              消息最终一致      适合通知类              XA      强一致，性能和运维成本高      15.8 团队与流程康威定律：系统架构往往反映组织沟通结构。团队模式：            模式      特点                  按层团队      前端组、后端组、DBA 组              按业务域团队      订单团队、商品团队              平台团队      提供网关、消息、监控      微服务要求：  服务有明确 Owner；  团队可独立开发、测试、发布；  接口变更走契约；  on-call 责任清晰；  公共库治理有平台团队。15.9 拆分实施路径1. 梳理业务流程和通用语言2. 画出限界上下文3. 标记数据所有权4. 识别高变更和高故障模块5. 选择第一个独立服务6. 建立契约和可观测性7. 数据迁移8. 双写或灰度切换9. 回归验证10. 清理旧代码第一个服务建议选择：  边界清晰；  依赖较少；  变更频繁；  可独立验证；  故障影响可控。不要先拆核心支付账务这类高风险能力。15.10 拆分评估表            维度      问题                  业务边界      是否有独立通用语言              数据边界      是否独立拥有表              接口契约      是否稳定              发布独立性      是否可独立上线              可伸缩性      是否有独立扩容需求              故障隔离      是否能独立降级              团队 ownership      是否有维护者              观测能力      trace/log/metric 是否完整              回滚方案      能否快速回滚              成本      是否值得      本章小结微服务拆分应以业务限界上下文和数据所有权为核心，从粗粒度服务开始，逐步建立独立数据库、契约、发布流程和观测体系。拆分不是越多越好，每增加一个服务都带来网络、事务、安全和排障成本。先证明边界稳定，再推进拆分。思考题  为什么不能按 Controller、Service、DAO 拆微服务？  如何判断服务粒度过细？  跨服务查询有哪些替代方案？  Saga 和 TCC 分别适合什么场景？  如何选择第一个拆分出来的服务？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。测试的目标是用可重复的方式证明行为正确，并在修改时及时暴露回归。Spring 项目测试的关键是分层：单元测试快而稳定，集成测试验证装配和外部契约，端到端测试少量覆盖关键链路。14.1 测试金字塔       E2E      /   \   API / 集成  /         \      单元测试            层      目标      速度                  单元测试      业务逻辑和领域规则      快              切片测试      Web、Data、JSON 等局部      较快              集成测试      容器装配、数据库、消息      慢              E2E      用户关键路径      最慢      数量建议：单元测试最多，集成测试适量，E2E 少而稳定。14.2 测试依赖&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;    &lt;scope&gt;test&lt;/scope&gt;&lt;/dependency&gt;&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-testcontainers&lt;/artifactId&gt;    &lt;scope&gt;test&lt;/scope&gt;&lt;/dependency&gt;&lt;dependency&gt;    &lt;groupId&gt;org.testcontainers&lt;/groupId&gt;    &lt;artifactId&gt;mysql&lt;/artifactId&gt;    &lt;scope&gt;test&lt;/scope&gt;&lt;/dependency&gt;常用库：            库      用途                  JUnit 5      测试框架              AssertJ      断言              Mockito      替身              JSONPath      JSON 断言              Testcontainers      真实依赖容器              Awaitility      异步断言              ArchUnit      架构约束      14.3 单元测试被测类：public class DiscountCalculator {    public BigDecimal calculate(UserLevel level, BigDecimal amount) {        return switch (level) {            case VIP -&gt; amount.multiply(new BigDecimal("0.85"));            case NORMAL -&gt; amount;        };    }}测试：class DiscountCalculatorTest {    @Test    void should_apply_vip_discount() {        DiscountCalculator calculator = new DiscountCalculator();        BigDecimal result = calculator.calculate(UserLevel.VIP, new BigDecimal("100.00"));        assertThat(result).isEqualByComparingTo("85.00");    }}单元测试不需要启动 Spring。大量 @SpringBootTest 会拖慢构建，也会让失败定位变模糊。14.4 Mockito@ExtendWith(MockitoExtension.class)class OrderServiceTest {    @Mock    OrderRepository repository;    @Test    void should_create_order() {        when(repository.nextId()).thenReturn(1L);        OrderService service = new OrderService(repository);        Order order = service.create(new CreateOrderCommand("u1", 2, 1));        assertThat(order.status()).isEqualTo(OrderStatus.CREATED);        verify(repository).save(any(Order.class));    }}原则：  不 mock 值对象；  不过度验证所有调用；  测试行为而不是实现细节；  不用 mock 代替设计问题；  异步逻辑用 Awaitility 验证。14.5 Web 切片测试@WebMvcTest(OrderController.class)class OrderControllerTest {    @Autowired    MockMvc mockMvc;    @MockBean    OrderService orderService;    @Test    void should_return_order() throws Exception {        when(orderService.get(1L)).thenReturn(new OrderView(1L, "CREATED"));        mockMvc.perform(get("/api/orders/1"))                .andExpect(status().isOk())                .andExpect(jsonPath("$.id").value(1))                .andExpect(jsonPath("$.status").value("CREATED"));    }}切片测试只加载 Web 相关 Bean，比完整启动更快，适合验证：  参数绑定；  校验；  状态码；  JSON 序列化；  异常处理；  安全规则。Spring Boot 新版本对 mock Bean 注解有调整，以当前测试 API 为准。14.6 Testcontainers@SpringBootTest@Testcontainersclass OrderIntegrationTests {    @Container    @ServiceConnection    static MySQLContainer&lt;?&gt; mysql = new MySQLContainer&lt;&gt;("mysql:8.4");    @Autowired    TestRestTemplate restTemplate;    @Test    void should_create_and_query_order() {        OrderView created = restTemplate.postForObject(                "/api/orders", new CreateOrderRequest("u1", 1L, 1), OrderView.class);        assertThat(created).isNotNull();    }}优点：  使用真实数据库；  避免 H2 与 MySQL 方言差异；  验证 SQL 和事务；  可复现环境。注意：  本机需要容器运行时；  CI 需要缓存镜像；  容器启动有成本；  测试数据要隔离；  不应每个方法都重建容器。14.7 数据库测试@DataJpaTest@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)@Testcontainersclass OrderRepositoryTests {    @Container    @ServiceConnection    static MySQLContainer&lt;?&gt; mysql = new MySQLContainer&lt;&gt;("mysql:8.4");    @Autowired    OrderRepository repository;    @Test    void should_find_by_user() {        repository.save(new OrderEntity("u1", OrderStatus.CREATED));        List&lt;OrderEntity&gt; orders = repository.findByUserId("u1");        assertThat(orders).hasSize(1);    }}事务回滚策略：            注解      默认行为                  @DataJpaTest      每个测试后回滚              @SpringBootTest      不自动回滚      共享容器的集成测试要显式清理数据，避免测试顺序耦合。14.8 契约测试生产者验证契约：@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)class ProducerContractTests {}消费者使用 mock server 验证：@AutoConfigureMockMvcclass ConsumerTests {}Spring Cloud Contract 的价值在于：  服务双方共享契约；  提前发现接口破坏；  消费者测试不依赖生产者部署；  CI 阻止不兼容变更。契约测试不能替代少量 E2E，但能显著减少跨服务联调成本。14.9 测试配置测试配置文件：spring:  datasource:    url: jdbc:tc:mysql:8.4:///orderProfile：@ActiveProfiles("test")建议：  不访问真实生产资源；  不依赖外部服务状态；  测试数据可重复构造；  不依赖执行顺序；  时间、随机数可注入；  网络调用使用 WireMock 或容器；  敏感信息不写入测试代码。14.10 CI 与质量门禁Maven：mvn testmvn verifyGradle：gradle testgradle check门禁建议：            指标      建议                  单元测试成功率      100%              核心业务行覆盖      按模块设定              集成测试      关键路径必跑              契约变更      双方确认              ArchUnit      架构规则              依赖漏洞      自动扫描              测试耗时      设置预算      覆盖率是结果指标，不是目标。为了覆盖率写无意义测试反而降低质量。本章小结Spring 测试应分层：领域和 Service 用快速单元测试，Web/Data 用切片测试，容器装配和外部依赖用 Testcontainers 集成测试，服务间契约用契约测试，端到端只保留关键路径。测试要稳定、可重复、与生产环境相似，并纳入 CI 门禁。思考题  什么时候不应使用 @SpringBootTest？  Mock 和 Testcontainers 各解决什么问题？  @DataJpaTest 的默认事务行为是什么？  契约测试解决微服务什么痛点？  如何保持 CI 测试速度快？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。消息用于服务解耦、削峰填谷和事件传播；调度用于定时任务、补偿任务和周期报表。两者都涉及重试、幂等、顺序、并发控制和死信处理。13.1 事件模型public record OrderCreatedEvent(        String eventId,        Long orderId,        String userId,        Instant occurredAt) {}事件字段建议：            字段      用途                  eventId      全局唯一，幂等              eventType      类型              occurredAt      业务发生时间              traceId      链路追踪              version      契约版本              payload      业务数据      事件名应表达事实：OrderCreatedPaymentSucceededShipmentDelivered避免 OrderChanged 这种信息量不足的名称。13.2 Spring 内部事件发布：@Servicepublic class OrderService {    private final ApplicationEventPublisher publisher;    @Transactional    public void create(Order order) {        repository.save(order);        publisher.publishEvent(new OrderCreatedEvent(...));    }}监听：@Componentpublic class InventoryListener {    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)    public void on(OrderCreatedEvent event) {        inventoryService.reserve(event.orderId());    }}区别：            注解      执行                  @EventListener      发布线程立即执行              @Async      提交线程池异步执行              @TransactionalEventListener      绑定事务阶段      默认 AFTER_COMMIT 只在同一线程的当前事务提交后执行。事务回滚时默认不调用。跨线程和异步事件要显式设计。13.3 Kafka 生产者配置：spring:  kafka:    producer:      bootstrap-servers: localhost:9092      key-serializer: org.apache.kafka.common.serialization.StringSerializer      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer      acks: all      properties:        enable.idempotence: true        max.in.flight.requests.per.connection: 5发送：@Servicepublic class OrderEventProducer {    private final KafkaTemplate&lt;String, OrderCreatedEvent&gt; kafkaTemplate;    public CompletableFuture&lt;SendResult&lt;String, OrderCreatedEvent&gt;&gt; send(OrderCreatedEvent event) {        return kafkaTemplate.send("order-events", event.orderId().toString(), event);    }}顺序通常依赖：  相同 key 写同一分区；  生产端幂等和 max in flight 配置；  不无限重试乱序；  消费端按分区顺序处理。13.4 Kafka 消费者@Componentpublic class OrderEventListener {    @KafkaListener(            topics = "order-events",            groupId = "inventory-service",            concurrency = "3")    public void on(ConsumerRecord&lt;String, OrderCreatedEvent&gt; record,                   Acknowledgment acknowledgment) {        handle(record.value());        acknowledgment.acknowledge();    }}提交策略：            模式      风险                  自动提交      可能丢消息或重复消费              先提交后处理      可能丢消息              先处理后提交      至少一次，可能重复      推荐至少一次 + 幂等。幂等键用 eventId 或业务唯一键，而不是 offset。13.5 重试与死信处理失败：业务消息  -&gt; 本地重试  -&gt; 延迟重试 topic  -&gt; 死信 topic  -&gt; 人工或自动补偿异常分类：            异常      处理                  参数缺失      记录死信，不重试              数据暂时不存在      延迟重试              下游超时      指数退避              序列化失败      死信并告警              未知异常      有限重试后死信      死信消息必须保留：原消息异常栈topic / partition / offset重试次数traceId时间13.6 幂等消费表结构：create table consumed_events (  event_id varchar(64) primary key,  consumer_group varchar(64) not null,  processed_at timestamp not null);create index idx_consumed_group_time  on consumed_events(consumer_group, processed_at);处理：@Transactionalpublic void handle(OrderCreatedEvent event) {    if (eventStore.exists(event.eventId(), "inventory-service")) {        return;    }    inventoryService.reserve(event.orderId());    eventStore.save(event.eventId(), "inventory-service");}注意：  业务写和幂等记录要在一个本地事务；  幂等表需要清理策略；  并发同一 key 可能需要唯一约束兜底；  重试不会破坏结果；  消费位移提交要晚于业务事务。13.7 Outbox 模式create table outbox_events (  event_id varchar(64) primary key,  event_type varchar(100) not null,  aggregate_id varchar(64) not null,  payload json not null,  status varchar(20) not null,  retry_count int default 0,  created_at timestamp not null,  sent_at timestamp null);事务内：@Transactionalpublic void create(Order order) {    orderRepository.save(order);    outboxRepository.save(OutboxEvent.from(order));}投递：扫描未发送 outbox  -&gt; 发送到 Kafka  -&gt; 标记 sent  -&gt; 失败递增 retry_count相比事务内直接发消息，Outbox 保证业务成功与事件产生的原子性。CDC 方案可以进一步减少扫描压力。13.8 Spring 调度启用：@EnableScheduling@SpringBootApplicationpublic class Application {}任务：@Componentpublic class SettlementJob {    @Scheduled(cron = "0 0 2 * * *", zone = "Asia/Shanghai")    public void settle() {        settlementService.settleYesterday();    }}固定延迟：@Scheduled(fixedDelay = 5, timeUnit = TimeUnit.MINUTES)public void reconcile() {}fixedRate 可能任务重叠，必须配置：spring.task.scheduling.pool.size=413.9 分布式调度多实例部署时，@Scheduled 默认每个实例都会执行。方案：            方案      特点                  ShedLock      数据库或 Redis 锁，简单              XXL-Job / ElasticJob      任务平台和分片              Kubernetes CronJob      平台调度              Quartz 集群      传统数据库锁      ShedLock 示例：@Scheduled(cron = "0 */5 * * * *")@SchedulerLock(name = "settlement", lockAtMostFor = "10m", lockAtLeastFor = "1m")public void settle() {}lockAtMostFor 必须大于最坏执行时间，否则锁提前释放导致重复执行。13.10 任务可观测性日志：job=start jobId=...job=end status=success cost=1200ms read=10000 written=9998job=end status=failed retryable=true指标：job_start_totaljob_success_totaljob_failure_totaljob_duration_secondsjob_last_success_timestampqueue_lagconsumer_retry_totaldead_letter_total告警：  任务超过窗口未成功；  消费 lag 持续增长；  死信增长；  消费失败率突增；  任务执行时间持续上升。本章小结消息系统应遵循至少一次投递、业务幂等、失败重试和死信治理。Spring 事件适合进程内解耦，跨服务事件使用 Kafka/RocketMQ 并结合 Outbox。调度任务在多实例环境必须加分布式锁或任务平台，并记录执行时长、结果和最后成功时间。思考题  @EventListener 和 @TransactionalEventListener 有什么区别？  如何保证 Kafka 消费幂等？  为什么推荐 Outbox 模式？  fixedRate 调度可能带来什么问题？  多实例定时任务如何避免重复执行？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。缓存用于降低延迟、减少数据库压力、保存临时计算结果。它同时引入一致性问题、过期策略、击穿穿透雪崩和容量治理。是否加缓存，应先量化慢在哪里。12.1 缓存层次请求  -&gt; 本地缓存  -&gt; 分布式缓存 Redis  -&gt; 数据库            缓存      特点                  本地 Caffeine      低延迟、进程内、容量受 JVM 堆限制              Redis      多实例共享、容量独立、网络开销              多级缓存      高性能但一致性和失效更复杂              CDN/网关缓存      静态或准静态内容      多数系统不应一开始就做复杂多级缓存。先 Redis 或 Caffeine，遇到明确收益再演进。12.2 Spring Cache 抽象启用：@EnableCaching@SpringBootApplicationpublic class Application {}使用：@Servicepublic class ProductService {    @Cacheable(cacheNames = "products", key = "#id")    public ProductView get(Long id) {        return repository.findView(id);    }    @CacheEvict(cacheNames = "products", key = "#id")    public void update(Long id, UpdateProductCommand command) {        repository.update(id, command);    }}常用注解：            注解      作用                  @Cacheable      先查缓存，未命中执行方法后写入              @CachePut      执行方法并更新缓存              @CacheEvict      删除缓存              @Caching      组合多个缓存操作              @CacheConfig      类级公共配置      12.3 key 生成默认 key 基于参数生成。复杂场景应显式：@Cacheable(cacheNames = "products", key = "'user:' + #userId + ':page:' + #page")public Page&lt;ProductView&gt; page(Long userId, int page) {}注意：  key 必须稳定序列化；  参数对象应实现 equals/hashCode；  key 要包含命名空间和版本；  避免无界 key；  key 中不要包含敏感明文。12.4 RedisCacheManager@Configurationpublic class CacheConfig {    @Bean    public RedisCacheManager cacheManager(RedisConnectionFactory factory) {        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()                .entryTtl(Duration.ofMinutes(10))                .disableCachingNullValues()                .computePrefixWith(name -&gt; "app:" + name + ":")                .serializeValuesWith(SerializationPair.fromSerializer(                        new GenericJackson2JsonRedisSerializer()));        Map&lt;String, RedisCacheConfiguration&gt; configs = Map.of(                "products", config.entryTtl(Duration.ofMinutes(5)),                "configs", config.entryTtl(Duration.ofHours(1)));        return RedisCacheManager.builder(factory)                .cacheDefaults(config)                .withInitialCacheConfigurations(configs)                .build();    }}序列化选择：            方式      特点                  JDK 序列化      简单但可读性差、跨版本风险              JSON      可读，需处理类型和兼容              GenericJackson2JsonRedisSerializer      带类型信息，注意类型 ID 风险              自定义 DTO 序列化      契约清晰，推荐核心数据      12.5 一致性策略常见顺序：先更新数据库，再删除缓存@Transactionalpublic void update(Long id, UpdateCommand command) {    repository.update(id, command);    cache.evict("product:" + id);}为什么常删缓存而不是更新：  避免并发写覆盖；  懒加载下次重建；  避免写冷数据；  删除语义简单。但该策略仍可能出现短暂不一致。更强方案：  延迟双删；  版本号；  TTL 兜底；  订阅 binlog/CDC 删除；  读写锁或串行化；  强一致读数据库。12.6 缓存问题            问题      定义      应对                  穿透      查询不存在的数据      缓存空值、布隆过滤器、参数校验              击穿      热 key 过期瞬间大量请求      互斥重建、逻辑过期、热点永不过期              雪崩      大量 key 同时过期      TTL 加随机、限流、熔断              污染      缓存错误数据      校验来源、版本、灰度              膨胀      key 无界增长      最大容量、淘汰策略      互斥重建示例：public Product get(Long id) {    String key = "product:" + id;    Product cached = redis.get(key);    if (cached != null) {        return cached;    }    String lockKey = "lock:" + key;    if (redis.setNx(lockKey, "1", Duration.ofSeconds(3))) {        try {            Product product = repository.find(id);            redis.set(key, product, ttlWithJitter());            return product;        } finally {            redis.delete(lockKey);        }    }    return repository.find(id);}12.7 TTL 与淘汰Redis 配置：maxmemory 2gbmaxmemory-policy allkeys-lru            策略      说明                  noeviction      内存满拒绝写入              allkeys-lru      所有 key 近似 LRU              volatile-lru      只淘汰有 TTL 的 key              allkeys-lfu      近似 LFU              volatile-ttl      淘汰剩余时间短的 key      应用侧建议：  每类数据单独 TTL；  基础 TTL 加随机偏移；  热点数据可逻辑过期；  定期统计 key 数量和内存；  不允许“永久 + 无界”的业务 key。12.8 Caffeine@Beanpublic Cache&lt;Long, ProductView&gt; productCache() {    return Caffeine.newBuilder()            .maximumSize(10_000)            .expireAfterWrite(Duration.ofMinutes(5))            .recordStats()            .build();}使用：public ProductView get(Long id) {    return cache.get(id, key -&gt; repository.findView(key));}指标：hitCountmissCounthitRateevictionCountloadAverageTime本地缓存适合：  读取热点；  数据可短暂不一致；  单实例或可广播失效；  数据量有限。12.9 多级缓存L1 Caffeine  -&gt; L2 Redis     -&gt; DB失效：DB 更新  -&gt; 删除 Redis  -&gt; 发布失效消息  -&gt; 各实例删除本地 L1问题：  消息丢失；  实例处理延迟；  时钟不同；  本地容量抖动；  版本不一致。多级缓存通常只用于极热点数据，且必须监控命中率和不一致窗口。12.10 缓存治理上线前检查：1. key 命名规范2. 最大 TTL 和最小 TTL3. 容量和淘汰策略4. 空值处理5. 热点保护6. 序列化兼容7. 权限和网络隔离8. 命中率指标9. 回源限流10. 回滚方案监控：cache_hit_ratecache_miss_countcache_eviction_countcache_get_latencycache_put_latencycache_redis_pool_activecache_redis_errorsorigin_query_count命中率低时要分析：  key 是否随机化过强；  TTL 是否过短；  容量是否不足；  业务是否本身冷数据多；  是否值得缓存。本章小结Spring Cache 提供统一缓存抽象，底层可用 Caffeine、Redis 等。生产缓存的关键不是注解，而是 key 设计、TTL、容量、一致性、穿透击穿雪崩和命中率监控。先量化收益再引入缓存，并保留数据库兜底和回滚能力。思考题  为什么常使用“更新数据库后删除缓存”？  缓存穿透、击穿、雪崩分别如何应对？  本地缓存和分布式缓存适合什么数据？  多级缓存的一致性难点是什么？  缓存命中率低时如何分析？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。数据访问层的目标不是消除 SQL，而是把连接管理、事务边界、异常转换、资源释放和重复模板代码交给框架，让开发者专注业务模型和 SQL 质量。11.1 技术选择            技术      适合场景                  Spring JDBC / JdbcClient      明确 SQL 控制、复杂查询、低抽象成本              Spring Data JPA      领域模型稳定、CRUD 丰富、对象图清晰              MyBatis / MyBatis-Plus      SQL 可控、团队熟悉、复杂报表多              R2DBC      Reactive 数据访问              JOOQ      类型安全 SQL      选择原则：  团队能力；  SQL 复杂度；  性能要求；  领域模型复杂度；  维护周期；  招聘成本；  现有代码风格。不要因为某个框架流行就频繁迁移。11.2 DataSource 与连接池常用 HikariCP 配置：spring:  datasource:    url: jdbc:mysql://localhost:3306/order?useSSL=false&amp;serverTimezone=Asia/Shanghai    username: order    password: ${DB_PASSWORD}    hikari:      pool-name: order-hikari      maximum-pool-size: 20      minimum-idle: 5      connection-timeout: 2000      validation-timeout: 1000      max-lifetime: 1800000      leak-detection-threshold: 10000连接池不是越大越好。过大可能造成数据库上下文切换和锁等待，过小会造成请求排队。应基于数据库最大连接数、服务实例数和单请求持连接时间计算。估算：每实例最大连接 * 实例数 + 运维预留 &lt;= 数据库可承受连接11.3 JdbcClientSpring Framework 6.1 引入 JdbcClient，提供更简洁的 JDBC API：@Repositorypublic class JdbcOrderRepository {    private final JdbcClient jdbcClient;    public JdbcOrderRepository(JdbcClient jdbcClient) {        this.jdbcClient = jdbcClient;    }    public Optional&lt;Order&gt; findById(Long id) {        return jdbcClient.sql("select id, user_id, status, amount from orders where id = :id")                .param("id", id)                .query(Order.class)                .optional();    }    public int insert(Order order) {        return jdbcClient.sql("""                insert into orders(id, user_id, status, amount, created_at)                values(:id, :userId, :status, :amount, :createdAt)                """)                .param("id", order.id())                .param("userId", order.userId())                .param("status", order.status())                .param("amount", order.amount())                .param("createdAt", order.createdAt())                .update();    }}SQL 必须使用绑定参数，避免字符串拼接导致注入。11.4 Spring Data JPA实体：@Entity@Table(name = "orders")public class OrderEntity {    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    private Long id;    @Column(nullable = false, length = 64)    private String userId;    @Enumerated(EnumType.STRING)    private OrderStatus status;    private BigDecimal amount;    @CreationTimestamp    private Instant createdAt;}Repository：public interface OrderRepository extends JpaRepository&lt;OrderEntity, Long&gt; {    List&lt;OrderEntity&gt; findByUserIdAndStatus(String userId, OrderStatus status, Pageable pageable);}适合：  领域模型与表结构较接近；  常规 CRUD 和简单查询；  持久化 dirty tracking 有价值；  团队理解 Hibernate 生命周期。复杂报表、批量大写入、多表优化 SQL 时，应使用投影 DTO、原生 SQL 或 JDBC。11.5 N+1 问题错误代码：List&lt;OrderEntity&gt; orders = repository.findAll();for (OrderEntity order : orders) {    total += order.getItems().size(); // 每次可能触发一条 SQL}解决：@EntityGraph(attributePaths = "items")List&lt;OrderEntity&gt; findByUserId(String userId);或 JPQL fetch join：@Query("select distinct o from OrderEntity o join fetch o.items where o.userId = :userId")List&lt;OrderEntity&gt; findWithItems(String userId);开启 SQL 日志是发现 N+1 的第一步：logging.level.org.hibernate.SQL=DEBUGlogging.level.org.hibernate.orm.jdbc.bind=TRACEHibernate 5 与 6 的绑定日志参数不同，需要按版本调整 logger。11.6 事务基础声明式事务：@Servicepublic class OrderService {    @Transactional    public Order create(CreateOrderCommand command) {        Order order = Order.create(command);        repository.save(order);        outboxRepository.save(OrderEvent.outbox(order));        return order;    }}流程：TransactionInterceptor  -&gt; 获取连接并关闭自动提交  -&gt; 执行业务  -&gt; 正常提交  -&gt; 异常按回滚规则回滚  -&gt; 释放连接事务应尽量短：  不在事务中调用慢 RPC；  不在事务中处理大文件；  不在事务中发送不可回滚消息；  批量任务分批提交；  查询方法按需只读事务。11.7 传播行为            传播行为      说明                  REQUIRED      当前有事务则加入，否则新建              REQUIRES_NEW      挂起当前事务，新建事务              NESTED      保存点嵌套，依赖驱动支持              SUPPORTS      有事务则加入，没有则非事务执行              NOT_SUPPORTED      挂起事务，非事务执行              NEVER      存在事务则报错              MANDATORY      必须已有事务，否则报错      示例：@Transactional(propagation = Propagation.REQUIRES_NEW)public void saveAudit(AuditRecord record) {}REQUIRES_NEW 会占用第二个连接，高并发下容易造成连接池耗尽。操作日志不一定必须独立事务，可通过异步事件或最终一致方案处理。11.8 隔离级别            隔离级别      常见问题                  READ UNCOMMITTED      可能脏读              READ COMMITTED      避免脏读，可能不可重复读              REPEATABLE READ      避免不可重复读，幻读行为与数据库实现有关              SERIALIZABLE      强隔离，代价高      Spring 可声明：@Transactional(isolation = Isolation.READ_COMMITTED)实际隔离效果还取决于数据库引擎和配置。MySQL InnoDB 的实现与 SQL 标准 isolation name 不完全一一对应，需要结合当前数据库文档分析。11.9 事务失效场景常见错误：@Servicepublic class OrderService {    @Transactional    public void create(Order order) {        repository.save(order);        this.sendNotification(order); // 自调用    }    @Transactional(propagation = Propagation.REQUIRES_NEW)    public void sendNotification(Order order) {    }}失效原因：            场景      原因                  自调用      不经过代理              非 public 方法      Spring 事务代理默认不支持              异常被 catch 吞掉      拦截器看不到异常              抛出受检异常      默认不回滚，需 rollbackFor              Bean 未被 Spring 管理      没有代理              数据源/事务管理器配置错误      连接不在事务上下文              多线程执行      事务绑定 ThreadLocal      正确处理受检异常：@Transactional(rollbackFor = Exception.class)public void importOrders(File file) throws IOException {}11.10 分布式数据一致性本地事务不能覆盖多个数据库或多个服务。常见方案：            方案      适用                  本地消息表 + 异步投递      可接受最终一致              Outbox + CDC      消息可靠性高              TCC      需要强业务补偿              Saga      长流程编排              2PC/XA      强一致，吞吐和运维成本高      Outbox 示例：本地事务：  写 orders  写 outbox_events后台任务或 CDC：  读取 outbox  发送 Kafka  标记已发送不要在本地事务提交前直接发送消息，因为消息发送成功而事务回滚会造成幻象消息。11.11 数据访问监控指标：hikaricp_connections_activehikaricp_connections_pendinghikaricp_connections_timeoutjdbc_query_secondsjdbc_query_totalspring_data_repository_seconds慢 SQL：  开启数据库慢日志；  应用记录 SQL 耗时；  使用 APM 或 Micrometer Tracing；  定期审查执行计划；  加索引要评估写入成本。连接池等待告警：active == maximumpending &gt; 0connection timeout count 增长本章小结Spring 提供了从 JDBC 到 JPA 的多层次数据访问抽象，核心价值是资源管理、事务边界和异常统一。生产中要控制连接池规模、避免 N+1 和长事务，理解传播行为和事务失效原因。跨库跨服务一致性应引入 Outbox、Saga 或 TCC 等方案，而不是期待 @Transactional 覆盖分布式场景。思考题  如何估算 HikariCP 最大连接数？  JPA 和 JDBC 各适合什么场景？  REQUIRES_NEW 有什么风险？  哪些常见写法会导致事务失效？  为什么推荐 Outbox 而不是事务内直接发消息？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。Web 层是系统的入口，也是参数校验、异常处理、安全、文档、监控和幂等设计的汇聚点。Spring MVC 提供了强大的注解模型，但接口是否稳定、可维护，取决于团队契约设计。10.1 第一个 REST 接口@RestController@RequestMapping("/api/orders")public class OrderController {    private final OrderService orderService;    public OrderController(OrderService orderService) {        this.orderService = orderService;    }    @GetMapping("/{id}")    public OrderView get(@PathVariable Long id) {        return orderService.get(id);    }    @PostMapping    @ResponseStatus(HttpStatus.CREATED)    public OrderView create(@Valid @RequestBody CreateOrderRequest request) {        return orderService.create(request);    }}依赖：&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-starter-validation&lt;/artifactId&gt;&lt;/dependency&gt;10.2 请求映射            注解      说明                  @GetMapping      查询              @PostMapping      创建或非幂等动作              @PutMapping      全量更新              @PatchMapping      部分更新              @DeleteMapping      删除              @RequestMapping      通用映射      参数绑定：            注解      来源                  @PathVariable      路径              @RequestParam      Query              @RequestBody      请求体              @RequestHeader      Header              @CookieValue      Cookie              @ModelAttribute      对象绑定              @RequestPart      multipart      URL 设计建议：GET    /api/orders                 查询列表GET    /api/orders/{id}            查询详情POST   /api/orders                 创建PUT    /api/orders/{id}            全量更新PATCH  /api/orders/{id}            部分更新POST   /api/orders/{id}/cancel     非幂等业务动作10.3 请求与响应模型不要直接暴露实体：public record CreateOrderRequest(        @NotBlank @Size(max = 64) String userId,        @NotNull @Positive Long skuId,        @Min(1) @Max(100) int quantity) {}public record OrderView(        Long id,        String status,        BigDecimal amount,        Instant createdAt) {}好处：  避免 JPA 懒加载序列化问题；  隐藏内部字段；  API 契约稳定；  输入输出权限不同；  更容易编写 OpenAPI 文档。金额与时间建议：            类型      建议                  金额      BigDecimal 或明确的最小单位整数              时间      Instant，序列化为 ISO-8601              枚举      字符串或明确编码              ID      明确类型和语义      10.4 参数校验public record CreateAddressRequest(        @NotBlank String country,        @NotBlank String city,        @NotBlank String detail,        @Pattern(regexp = "^1[3-9]\\d{9}$") String phone) {}嵌套校验：public record CreateOrderRequest(        @NotBlank String userId,        @NotNull @Valid Address address) {}校验异常处理：@RestControllerAdvicepublic class GlobalExceptionHandler {    @ExceptionHandler(MethodArgumentNotValidException.class)    @ResponseStatus(HttpStatus.BAD_REQUEST)    public ErrorResponse handleValidation(MethodArgumentNotValidException ex) {        List&lt;FieldError&gt; errors = ex.getBindingResult().getFieldErrors();        return new ErrorResponse("VALIDATION_ERROR", errors.get(0).getDefaultMessage());    }}校验要同时考虑：  语法校验；  业务规则校验；  权限校验；  幂等校验；  大小限制。10.5 统一响应常见格式：public record ApiResponse&lt;T&gt;(        String code,        String message,        T data,        String traceId) {    public static &lt;T&gt; ApiResponse&lt;T&gt; ok(T data) {        return new ApiResponse&lt;&gt;("OK", "success", data, TraceContext.currentId());    }}是否包装统一响应要看团队契约。若客户端明确依赖 HTTP 状态码和资源模型，强制包装会破坏 REST 风格；若企业内部已有统一网关和 SDK，统一结构可能更方便。关键是保持一致，不混用多套约定。10.6 异常处理@RestControllerAdvicepublic class ApiExceptionHandler {    @ExceptionHandler(OrderNotFoundException.class)    @ResponseStatus(HttpStatus.NOT_FOUND)    public ErrorResponse notFound(OrderNotFoundException ex) {        return new ErrorResponse("ORDER_NOT_FOUND", ex.getMessage());    }    @ExceptionHandler(Exception.class)    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)    public ErrorResponse unexpected(Exception ex) {        log.error("unexpected error", ex);        return new ErrorResponse("INTERNAL_ERROR", "system busy");    }}原则：  业务异常返回业务码；  系统异常不泄露堆栈；  记录 traceId；  日志包含请求上下文；  客户端能看到稳定契约；  服务端保留完整堆栈。10.7 拦截器与过滤器Filter 位于 Servlet 层：@Componentpublic class TraceIdFilter extends OncePerRequestFilter {    @Override    protected void doFilterInternal(HttpServletRequest request,                                    HttpServletResponse response,                                    FilterChain chain) throws ServletException, IOException {        String traceId = Optional.ofNullable(request.getHeader("X-Trace-Id"))                .orElseGet(() -&gt; UUID.randomUUID().toString());        TraceContext.set(traceId);        response.setHeader("X-Trace-Id", traceId);        try {            chain.doFilter(request, response);        } finally {            TraceContext.clear();        }    }}Interceptor 位于 Spring MVC 层：@Componentpublic class AuthInterceptor implements HandlerInterceptor {    @Override    public boolean preHandle(HttpServletRequest request,                             HttpServletResponse response,                             Object handler) {        return checkToken(request);    }}选择：            组件      适合                  Filter      TraceId、编码、CORS、请求日志              Interceptor      认证授权、_handler 级监控              AOP      Service 方法级横切逻辑              ControllerAdvice      异常与模型绑定      10.8 分页与列表请求：public record PageQuery(        @Min(1) Integer page,        @Min(1) @Max(100) Integer size,        String sort) {    public int page() {        return page == null ? 1 : page;    }    public int size() {        return size == null ? 20 : size;    }}响应：public record PageResponse&lt;T&gt;(        List&lt;T&gt; items,        long total,        int page,        int size) {}必须限制最大 size，否则深分页或超大 pageSize 会拖垮数据库和应用。10.9 幂等设计创建订单需要幂等键：POST /api/ordersIdempotency-Key: 2b0d7a1e-...处理流程：1. 查询幂等键   -&gt; 已完成：返回原结果   -&gt; 处理中：返回 409 或稍后重试2. 插入幂等记录，可加唯一约束3. 执行业务事务4. 更新结果并非所有接口都必须幂等。支付、扣库存、发券等写操作必须有明确幂等策略；纯查询天然幂等。10.10 文件上传下载限制大小：spring.servlet.multipart.max-file-size=20MBspring.servlet.multipart.max-request-size=25MB上传：@PostMapping(value = "/files", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)public FileView upload(@RequestParam("file") MultipartFile file) {    return storageService.upload(file);}注意：  校验扩展名和 Content-Type；  校验文件头；  重命名存储；  限制大小；  不把文件全部读入内存；  下载大文件使用流；  病毒扫描按需接入；  权限校验不能只依赖 URL。10.11 OpenAPI依赖：&lt;dependency&gt;    &lt;groupId&gt;org.springdoc&lt;/groupId&gt;    &lt;artifactId&gt;springdoc-openapi-starter-webmvc-ui&lt;/artifactId&gt;    &lt;version&gt;...&lt;/version&gt;&lt;/dependency&gt;注解：@Operation(summary = "Create order")@PostMappingpublic OrderView create(@RequestBody CreateOrderRequest request) {}生产建议：  API 变更走契约评审；  破坏性变更升版本；  Swagger UI 不暴露到公网；  文档与代码同步生成；  异常模型也写入文档。本章小结Spring MVC 提供了注解化的 REST 开发模型，生产接口还需要统一模型、参数校验、异常处理、分页限制、幂等键、日志追踪和安全控制。Web 层应保持薄，只做协议适配和校验，业务规则下沉到 Service 或领域层，避免 Controller 膨胀。思考题  为什么不建议 Controller 直接返回数据库实体？  Filter、Interceptor、AOP 各适合什么横切逻辑？  如何设计创建订单接口的幂等？  分页接口为什么必须限制最大 size？  统一响应包装有哪些取舍？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。Starter 是依赖聚合和自动装配的组合包，目标是让使用方“引入依赖、写少量配置、即可使用能力”。设计良好的 Starter 应该有清晰边界、可控默认值、完善提示和测试。9.1 Starter 的组成demo-sms-spring-boot-starter  |-- pom.xml  |-- autoconfigure module  |   |-- SmsAutoConfiguration  |   |-- SmsProperties  |   +-- META-INF/spring/*.imports  |-- starter module  |   +-- 依赖聚合  +-- tests简单项目可以合并为一个模块；多模块便于把 API、实现和自动配置分离。职责：            模块      职责                  api      对外接口与 DTO              core      核心实现              autoconfigure      Spring 条件装配              starter      只做依赖聚合      9.2 命名规范官方 Starter：spring-boot-starter-webspring-boot-starter-data-jpa第三方 Starter：xxx-spring-boot-starter不要使用 spring-boot-starter-xxx 冒充官方包。命名清晰有助于排查依赖来源和版本冲突。9.3 设计原则  只自动配置自己负责的能力；  不扫描用户业务包；  默认值安全保守；  允许用户覆盖 Bean；  属性集中且有校验；  日志有开关；  外部调用有超时；  不强制绑定特定日志实现；  提供 IDE 提示；  版本矩阵清晰。尤其不要在 Starter 中使用：@ComponentScan("com.example")这会意外扫描使用方代码，造成不可控 Bean 注册。9.4 完整示例属性：@ConfigurationProperties(prefix = "demo.sms")@Validatedpublic record SmsProperties(        @NotBlank String endpoint,        @NotNull Duration connectTimeout,        @NotNull Duration readTimeout,        @Valid Client client) {    public record Client(            @Min(1) @Max(200) int poolSize,            boolean compression) {    }}自动配置：@AutoConfiguration@EnableConfigurationProperties(SmsProperties.class)@ConditionalOnClass(SmsClient.class)public class SmsAutoConfiguration {    @Bean    @ConditionalOnMissingBean(SmsClient.class)    public SmsClient smsClient(SmsProperties properties) {        return SmsClient.builder()                .endpoint(properties.endpoint())                .connectTimeout(properties.connectTimeout())                .readTimeout(properties.readTimeout())                .build();    }}使用方配置：demo:  sms:    endpoint: https://sms.example.com    connect-timeout: 1s    read-timeout: 3s    client:      pool-size: 20      compression: true9.5 依赖管理Starter POM 示例：&lt;dependencies&gt;    &lt;dependency&gt;        &lt;groupId&gt;com.example&lt;/groupId&gt;        &lt;artifactId&gt;demo-sms-spring-boot-autoconfigure&lt;/artifactId&gt;    &lt;/dependency&gt;    &lt;dependency&gt;        &lt;groupId&gt;com.example&lt;/groupId&gt;        &lt;artifactId&gt;demo-sms-core&lt;/artifactId&gt;    &lt;/dependency&gt;&lt;/dependencies&gt;版本策略：            依赖      建议                  Spring Boot      provided 或由父工程管理              Jackson      尽量交给 Boot 管理              日志 API      使用 SLF4J              HTTP 客户端      声明明确版本              工具库      避免传递大量无关依赖      不要把内部实现依赖全部传递给使用方。9.6 条件设计常见开关：@ConditionalOnProperty(prefix = "demo.sms", name = "enabled", havingValue = "true", matchIfMissing = true)matchIfMissing = true 表示默认开启。选择默认值时要考虑：  未配置时是否应该自动创建连接；  缺少 endpoint 时是否应该失败；  测试环境是否需要禁用；  是否会让使用方启动变慢。推荐：必需 endpoint 缺失 -&gt; 启动失败enabled=false -&gt; 不创建可选行为 -&gt; 默认关闭外部连接 -&gt; 默认懒创建9.7 生命周期与资源释放客户端持有连接池或调度器时应支持关闭：@Bean(destroyMethod = "close")public SmsClient smsClient(SmsProperties properties) {    return SmsClient.create(properties);}如果对象没有公开关闭方法，可以：@Bean(destroyMethod = "")public SmsClient smsClient() {}然后通过生命周期 Bean 管理资源。资源关闭日志要有实例标识，便于定位泄漏。9.8 可观测性暴露指标：@Beanpublic SmsMetrics smsMetrics(MeterRegistry registry) {    return new SmsMetrics(registry);}建议指标：sms_client_request_secondssms_client_request_totalsms_client_failure_totalsms_client_pool_active日志规范：  不打印手机号全文；  不打印验证码；  记录 requestId；  失败记录原因；  debug 日志可关闭。9.9 文档与提示必须提供：  引入坐标；  支持版本矩阵；  配置表；  默认值；  覆盖 Bean 的方式；  行为开关；  指标；  常见问题；  安全注意事项；  兼容性变更。生成配置提示：&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-configuration-processor&lt;/artifactId&gt;    &lt;optional&gt;true&lt;/optional&gt;&lt;/dependency&gt;构建后生成：META-INF/spring-configuration-metadata.json9.10 测试矩阵至少覆盖：            场景      断言                  默认条件      Bean 创建              disabled      Bean 不创建              用户覆盖      用户 Bean 生效              缺少 endpoint      启动失败              超时非法      校验失败              关闭上下文      资源释放              多上下文      无静态污染              Web/非 Web      行为一致              不同 Boot 版本      兼容矩阵      测试用户覆盖：@SpringBootTest@Import(CustomSmsConfiguration.class)class OverrideTests {    @Autowired    private SmsClient smsClient;    @Test    void shouldUseCustomClient() {        assertThat(smsClient).isInstanceOf(CustomSmsClient.class);    }}本章小结Starter 的价值是把复杂集成收敛为依赖、配置和默认行为。设计时要坚持职责清晰、不扫描用户包、默认值保守、属性可校验、资源可释放、指标可观测。Spring Boot 3 的自动装配注册使用 AutoConfiguration.imports，并通过完整测试矩阵保证版本兼容。思考题  为什么 Starter 不应该包含宽泛的 @ComponentScan？  官方和第三方 Starter 命名有什么区别？  如何设计必需配置缺失时的行为？  Starter 应该暴露哪些指标？  用户覆盖 Bean 的能力为什么重要？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。自动装配是 Spring Boot 的核心能力：根据 classpath、已有 Bean、属性条件和环境类型，自动创建一组合理的默认配置。它不是魔法，而是条件化 BeanDefinition 注册机制。8.1 @SpringBootApplication@SpringBootConfiguration@EnableAutoConfiguration@ComponentScanpublic @interface SpringBootApplication {}职责：            注解      作用                  @SpringBootConfiguration      声明配置类              @EnableAutoConfiguration      开启自动装配              @ComponentScan      扫描主类包及子包      如果主类放在错误包下，例如放在 com.example，业务代码在 com.example.order，没有显式扫描参数时默认能扫到；反之主类放在深层包而业务在外层包，就扫不到。8.2 自动装配入口Spring Boot 3 使用：META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importsSpring Boot 2 使用：META-INF/spring.factories自动配置类示例：@AutoConfiguration@ConditionalOnClass(ObjectMapper.class)@ConditionalOnMissingBean(ObjectMapper.class)public class JacksonAutoConfiguration {    @Bean    public ObjectMapper objectMapper() {        return new ObjectMapper();    }}自动配置不是全部加载，而是逐个评估条件。8.3 常用条件            注解      条件                  @ConditionalOnClass      类存在              @ConditionalOnMissingClass      类不存在              @ConditionalOnBean      Bean 存在              @ConditionalOnMissingBean      Bean 不存在              @ConditionalOnProperty      属性匹配              @ConditionalOnResource      资源存在              @ConditionalOnWebApplication      Web 应用              @ConditionalOnNotWebApplication      非 Web 应用              @ConditionalOnExpression      SpEL 为 true              @AutoConfigureBefore / @AutoConfigureAfter      配置顺序      示例：@Bean@ConditionalOnProperty(prefix = "app.cache", name = "enabled", havingValue = "true")public LocalCache localCache() {    return new LocalCache();}8.4 自动装配报告启动时开启 debug：java -jar app.jar --debug输出：Positive matches:  JacksonAutoConfiguration matched:     - @ConditionalOnClass found required class 'com.fasterxml.jackson.databind.ObjectMapper'Negative matches:  DataSourceAutoConfiguration did not match:     - @ConditionalOnClass did not find required class 'javax.sql.DataSource'也可以通过 Actuator：/actuator/conditions排查思路：看配置类是否匹配  -&gt; 看 Positive / Negative 原因  -&gt; 检查依赖  -&gt; 检查属性  -&gt; 检查用户是否覆盖 Bean8.5 用户配置优先自动配置通常使用 @ConditionalOnMissingBean：@Bean@ConditionalOnMissingBean(DataSource.class)public DataSource defaultDataSource() {    return embeddedDataSource();}用户覆盖：@Configurationpublic class DataSourceConfig {    @Bean    public DataSource dataSource() {        return customDataSource();    }}这样用户配置优先，框架提供默认值。为了避免误覆盖，覆盖重要 Bean 时应清楚版本行为和配置类的 @AutoConfigureBefore 顺序。8.6 自动配置顺序@AutoConfiguration@AutoConfigureAfter(CacheAutoConfiguration.class)public class ReportAutoConfiguration {}顺序影响：  @ConditionalOnBean 的判断结果；  Bean 初始化顺序；  代理生成；  属性绑定；  Bean 覆盖关系。在 Starter 中应显式声明顺序，而不是依赖偶然加载顺序。8.7 编写一个自动配置业务客户端：public class SmsClient {    private final String endpoint;    private final RestClient restClient;    public SmsClient(String endpoint, RestClient restClient) {        this.endpoint = endpoint;        this.restClient = restClient;    }    public void send(String phone, String content) {        restClient.post().uri(endpoint).body(Map.of("phone", phone, "content", content)).retrieve().toBodilessEntity();    }}配置属性：@ConfigurationProperties(prefix = "sms")public record SmsProperties(String endpoint, Duration timeout) {}自动配置：@AutoConfiguration@EnableConfigurationProperties(SmsProperties.class)@ConditionalOnClass(SmsClient.class)public class SmsAutoConfiguration {    @Bean    @ConditionalOnMissingBean(SmsClient.class)    public SmsClient smsClient(SmsProperties properties, RestClient.Builder builder) {        return new SmsClient(properties.endpoint(), builder.build());    }}Spring Boot 3 注册文件：src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports内容：com.example.sms.SmsAutoConfiguration8.8 测试自动配置验证默认创建：@SpringBootTest(properties = "sms.endpoint=http://localhost:8080")class SmsAutoConfigurationTests {    @Autowired    private SmsClient smsClient;    @Test    void shouldCreateDefaultClient() {        assertThat(smsClient).isNotNull();    }}验证条件不满足：@SpringBootTest(properties = "sms.enabled=false")class SmsDisabledTests {    @Autowired    private ApplicationContext context;    @Test    void shouldNotCreateClient() {        assertThat(context.containsBean("smsClient")).isFalse();    }}必须覆盖：  默认创建；  用户覆盖；  条件不满足；  配置校验失败；  版本兼容。8.9 常见坑            问题      原因                  自动配置未生效      注册文件路径错误              条件不匹配      依赖缺失、属性错误              Bean 被意外覆盖      @ConditionalOnMissingBean              顺序不稳定      未声明 @AutoConfigureAfter              属性没有提示      缺少 metadata              版本不兼容      Starter 与 Boot 版本不匹配              打包后可用 IDE 不可用      resources 未进入 jar      配置提示依赖：&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-configuration-processor&lt;/artifactId&gt;    &lt;optional&gt;true&lt;/optional&gt;&lt;/dependency&gt;本章小结自动装配通过注册文件和条件注解，把常见配置变成可推导的默认行为。Spring Boot 3 使用 AutoConfiguration.imports，旧版本使用 spring.factories。排查时要善用 debug 自动装配报告，理解 Positive/Negative matches。设计 Starter 时应显式声明条件、顺序、属性绑定和测试矩阵。思考题  @ComponentScan 和自动装配有什么区别？  Spring Boot 2 与 3 的自动装配注册方式有何不同？  @ConditionalOnMissingBean 为什么能让用户配置优先？  自动配置顺序为什么会影响条件判断？  如何为 Starter 写完整自动化测试？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。SpringApplication.run 看似只有一行，实际完成了环境准备、上下文创建、Bean 加载、内嵌服务器启动和生命周期回调。掌握这条链路，可以定位启动失败、启动慢、配置未生效、端口冲突、优雅停机等常见问题。7.1 启动入口@SpringBootApplicationpublic class OrderApplication {    public static void main(String[] args) {        SpringApplication.run(OrderApplication.class, args);    }}也可以显式定制：SpringApplication app = new SpringApplication(OrderApplication.class);app.setApplicationType(ApplicationType.SERVLET);app.setBannerMode(Banner.Mode.OFF);app.setWebApplicationType(WebApplicationType.SERVLET);app.addListeners(new StartupListener());app.run(args);7.2 SpringApplication 构造阶段构造 SpringApplication 时会推断：  应用类型：Servlet、Reactive、普通应用；  初始化器；  监听器；  主类。判断依据来自 classpath：存在 Servlet 类 -&gt; SERVLET存在 Reactive 相关类且不存在 Servlet -&gt; REACTIVE都不存在 -&gt; NONE因此引入或移除依赖可能改变上下文类型，进而影响自动装配结果。7.3 run 主线简化流程：1. StopWatch 计时2. 获取 SpringApplicationRunListeners3. prepareEnvironment4. configureIgnoreBeanInfo5. printBanner6. createApplicationContext7. prepareContext8. refreshContext9. afterRefresh10. listeners.started11. callRunners12. listeners.ready关键扩展点：            阶段      扩展                  环境准备后      ApplicationEnvironmentPreparedEvent              上下文准备      ApplicationContextInitializer              上下文加载      ApplicationPreparedEvent              刷新完成      ApplicationStartedEvent              Runner 前      ApplicationRunner / CommandLineRunner              完全就绪      ApplicationReadyEvent      7.4 环境准备ApplicationArguments  -&gt; ConfigurableEnvironment  -&gt; configureEnvironment  -&gt; 配置 conversion service  -&gt; 绑定 spring.main  -&gt; EnvironmentPostProcessor常见配置源会进入 Environment：  命令行属性；  系统属性；  环境变量；  application.yml；  profile 文件；6.配置中心扩展的 PropertySource。Spring Boot 的配置文件加载由 ConfigDataEnvironmentPostProcessor 主导。自定义 EnvironmentPostProcessor 可以补充属性源：public class SecurePropertyEnvironmentPostProcessor implements EnvironmentPostProcessor {    @Override    public void postProcessEnvironment(ConfigurableEnvironment environment,                                       SpringApplication application) {        Map&lt;String, Object&gt; props = Map.of("app.secret-source", "vault");        environment.getPropertySources().addFirst(                new MapPropertySource("secureProperties", props));    }}注册文件：src/main/resources/META-INF/spring.factoriesorg.springframework.boot.env.EnvironmentPostProcessor=\com.example.SecurePropertyEnvironmentPostProcessorSpring Boot 3 中部分机制从 spring.factories 迁移到不同文件，需按当前版本文档注册。7.5 上下文创建与刷新Servlet 应用通常创建：AnnotationConfigServletWebServerApplicationContextrefreshContext 会执行 Spring 容器 refresh，并在 onRefresh 阶段创建 WebServer。ServletWebServerApplicationContext  -&gt; onRefresh  -&gt; createWebServer  -&gt; TomcatServletWebServerFactory  -&gt; getTomcatWebServer容器启动时机：            阶段      状态                  createWebServer      服务器对象创建              finishRefresh      WebServer 启动              ApplicationReadyEvent      应用完全就绪      端口绑定失败通常发生在服务器启动阶段。7.6 应用事件监听@Componentpublic class StartupEventListener {    @EventListener    public void onReady(ApplicationReadyEvent event) {        System.out.println("application ready");    }}注意：@Component 监听器通常在 Bean 初始化后才能接收后续事件。如果要在环境准备、上下文创建前监听，需要通过 SpringApplication.addListeners 或注册文件添加全局 Listener。事件顺序：ApplicationStartingEventApplicationEnvironmentPreparedEventApplicationPreparedEventApplicationStartedEventApplicationReadyEvent关闭时：ContextClosedEvent7.7 Runner@Componentpublic class CacheWarmUpRunner implements ApplicationRunner {    @Override    public void run(ApplicationArguments args) {        warmUp();    }}执行时机在 ApplicationStartedEvent 之后、ApplicationReadyEvent 之前。因此 Runner 阻塞会推迟就绪时间。建议：  只做必要初始化；  慢速预热异步执行；  外部调用设置超时；  记录启动阶段耗时；  失败策略明确：阻止启动还是降级运行。7.8 启动耗时分析开启启动诊断：logging.level.org.springframework.boot.context.config=DEBUGJava Flight Recorder：java -XX:StartFlightRecording=filename=startup.jfr,settings=profile \  -jar app.jar常见慢点：            慢点      现象                  类扫描过宽      主类放在根包外或扫描 com.*              Bean 过多      大量自动装配和第三方客户端              初始化远程调用      Runner 中同步加载配置              数据库连接      初始化连接池或执行迁移              CPU limit 低      JIT 和类加载慢              磁盘慢      日志、证书、本地文件读取              依赖冲突      反复解析和失败重试      Spring Boot 也可以输出 Startup Endpoint：/actuator/startup该端点依赖 BufferingApplicationEventPublisher，并需要安全保护。7.9 常见启动失败            异常      方向                  Port already in use      端口冲突              UnsatisfiedDependencyException      Bean 缺失或循环依赖              InvalidConfigurationPropertyException      配置绑定错误              Cannot determine embedded database driver      缺数据库依赖或 URL              ClassCastException      依赖版本冲突              NoSuchMethodError      编译期与运行期版本不一致              FileNotFoundException      外部配置或证书路径错误              Unable to load ApplicationContext      通常看 caused by      排查原则：读完整 caused by  -&gt; 确认版本矩阵  -&gt; 查看自动装配报告  -&gt; 最小化复现工程开启自动装配报告：java -jar app.jar --debug7.10 嵌入式服务器选择常见服务器：            服务器      特点                  Tomcat      Spring Boot 默认，生态成熟              Jetty      轻量，传统支持广泛              Undertow      常用于追求低内存和高吞吐场景      排除 Tomcat 并引入 Jetty：&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;    &lt;exclusions&gt;        &lt;exclusion&gt;            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;            &lt;artifactId&gt;spring-boot-starter-tomcat&lt;/artifactId&gt;        &lt;/exclusion&gt;    &lt;/exclusions&gt;&lt;/dependency&gt;&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-starter-jetty&lt;/artifactId&gt;&lt;/dependency&gt;选择应基于压测，而不是传闻。本章小结Spring Boot 启动流程由 SpringApplication 引导，完成应用类型推断、环境准备、上下文创建、容器刷新、内嵌服务器启动和 Runner 执行。ApplicationReadyEvent 才是完全就绪信号。排查启动问题要抓住完整异常链、事件阶段和自动装配报告，优化启动时要区分必要初始化和可异步预热。思考题  ApplicationStartedEvent 和 ApplicationReadyEvent 有什么区别？  为什么 Runner 中不宜执行耗时预热？  内嵌服务器是在 refresh 的哪个阶段创建的？  如何分析 Spring Boot 启动慢？  引入依赖为什么可能改变应用类型？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。配置管理决定应用在不同环境中的行为。Spring Environment 把配置源抽象成 PropertySource，并通过统一的解析、优先级和类型转换机制提供给 Bean。6.1 配置来源常见来源：命令行参数SPRING_APPLICATION_JSON环境变量系统属性application-{profile}.ymlapplication.yml@PropertySource默认值不同版本和配置中心的优先级可能不同。确认优先级的可靠方式是输出 Environment 的 PropertySources。6.2 Environment 抽象@Componentpublic class ConfigPrinter {    private final ConfigurableEnvironment environment;    public ConfigPrinter(ConfigurableEnvironment environment) {        this.environment = environment;    }    public void print() {        environment.getPropertySources().forEach(ps -&gt;                System.out.println(ps.getName() + " = " + ps.getSource()));    }}核心接口：            接口      作用                  PropertyResolver      读取和解析属性              Environment      环境属性与 Profile              ConfigurableEnvironment      可修改 PropertySource              PropertySource      单个配置来源              MutablePropertySources      有序配置源集合      6.3 application.ymlspring:  application:    name: order-service  datasource:    url: jdbc:mysql://localhost:3306/order?useSSL=false    username: order    password: ${DB_PASSWORD}  jpa:    open-in-view: falsepay:  url: https://pay.example.com  timeout: 3s  retry-times: 2推荐：  使用 kebab-case；  有单位的时间用 Duration；  敏感信息来自环境变量或密钥系统；  每个环境只写差异配置；  配置键集中到 Properties 类。6.4 Profilespring:  profiles:    active: dev---spring:  config:    activate:      on-profile: devpay:  url: http://localhost:8081---spring:  config:    activate:      on-profile: prodpay:  url: https://pay.example.com激活方式：java -jar app.jar --spring.profiles.active=prodSPRING_PROFILES_ACTIVE=prod java -jar app.jar不要用 Profile 表达业务开关，例如 feature-pay-v2。Profile 适合环境差异，业务开关应有自己的配置键和灰度体系。6.5 配置绑定推荐 @ConfigurationProperties：@ConfigurationProperties(prefix = "pay")public record PayProperties(        URI url,        Duration timeout,        int retryTimes,        List&lt;String&gt; fallbackRegions) {}启用：@ConfigurationPropertiesScan@SpringBootApplicationpublic class Application {}校验：@Validated@ConfigurationProperties(prefix = "pay")public record PayProperties(        @NotNull URI url,        @NotNull Duration timeout,        @Min(0) @Max(5) int retryTimes) {}优点：  配置集中；  类型安全；  IDE 提示；  启动时校验；  易写单元测试。6.6 松绑定以下写法都可以绑定到 pay.retryTimes：pay.retry-times=3pay.retryTimes=3PAY_RETRY_TIMES=3环境变量通常使用大写下划线：SPRING_DATASOURCE_URLPAY_RETRY_TIMES注意：  不要混用多种风格；  Map 和 List 的绑定规则更细；  缩短环境变量名需要显式配置；  环境变量值都是字符串，绑定到集合或对象时要看清规则。6.7 占位符与默认值pay.url=${PAY_URL:https://default.example.com}app.name=${spring.application.name}app.description=${app.name} order backend支持 SpEL 的场景要谨慎使用：app.token-ttl=#{60 * 60}过度使用表达式会让配置难以追踪。简单占位符优先。6.8 配置中心本地配置之外，微服务常使用：            方案      特点                  Spring Cloud Config      Git 后端，配置版本化              Nacos      注册与配置一体              Consul      多云生态常用              Kubernetes ConfigMap      平台原生              Vault      密钥管理      优先级建议：本地代码：默认值和非敏感配置环境变量：简单覆盖配置中心：环境差异和动态开关密钥系统：密码、证书、Token不要把数据库密码提交到 Git，也不要把所有业务配置都堆进环境变量。6.9 动态配置配置中心刷新示例：@RefreshScope@Componentpublic class RiskClient {    private final String endpoint;    public RiskClient(@Value("${risk.endpoint}") String endpoint) {        this.endpoint = endpoint;    }}刷新后 @RefreshScope Bean 会被重建。要注意：  旧 Bean 中持有的状态会丢失；  依赖它的对象引用可能仍指向旧代理目标；  动态开关应原子读取；  重要配置变更要有审计和灰度；  连接类配置重建成本高。更简单的动态开关可以自己维护内存快照：@Componentpublic class FeatureFlags {    private final AtomicReference&lt;Map&lt;String, Boolean&gt;&gt; flags =            new AtomicReference&lt;&gt;(Map.of());    public void update(Map&lt;String, Boolean&gt; next) {        flags.set(Map.copyOf(next));    }    public boolean enabled(String key) {        return flags.get().getOrDefault(key, false);    }}6.10 配置安全常见风险：  明文密码提交仓库；  Actuator 暴露 env；  日志打印配置对象；  配置中心权限过宽；  配置变更没有审计；  测试配置误用于生产；  默认账号密码未修改。建议：  使用 Vault、KMS 或云 Secret；  日志脱敏；  限制 Actuator 端点；  配置中心按命名空间授权；  保存配置变更历史；  启动时校验生产必需键；  本地开发配置与生产密钥隔离。6.11 排查配置问题@Componentpublic class EnvironmentDiagnostics {    public void print(String key) {        Environment env;    }}更直接的做法：public void print(ConfigurableEnvironment env) {    System.out.println(env.getProperty("pay.timeout"));    env.getPropertySources().stream()       .filter(ps -&gt; ps.containsProperty("pay.timeout"))       .forEach(ps -&gt; System.out.println(ps.getName()));}Actuator 端点：/actuator/env/actuator/configprops生产上应关闭或严格保护这些端点。常见问题：            问题      原因                  配置未生效      优先级低、缩进错误、Profile 未激活              类型绑定失败      格式错误、单位缺失              环境变量没绑定      命名不符合松绑定规则              密码泄露      env 端点或日志输出              刷新无效      Bean 不在刷新范围或引用未更新      本章小结Spring Environment 把命令行、系统属性、环境变量、Profile 文件和配置中心统一成有序 PropertySource。业务配置应通过 @ConfigurationProperties 集中绑定和校验，敏感信息交给密钥系统。多环境使用 Profile，业务开关使用显式配置和动态刷新机制，并用安全手段控制配置可见性。思考题  PropertySource 的顺序为什么重要？  @Value 和 @ConfigurationProperties 各适合什么场景？  环境变量如何映射到嵌套配置？  @RefreshScope 有哪些副作用？  如何保证生产配置缺失时应用无法启动？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。AOP 用于把日志、监控、权限、事务、限流、审计这类横切逻辑从业务代码中抽离。Spring AOP 是运行时代理模型，理解代理边界比记住切点语法更重要。5.1 核心概念            概念      说明                  Joinpoint      程序执行点，Spring 主要是方法执行              Pointcut      匹配哪些 Joinpoint              Advice      匹配后执行的逻辑              Aspect      切面，切点与通知的组合              Target Object      被代理目标              AOP Proxy      容器暴露的代理对象              Weaver      织入者，Spring 在运行时创建代理      常用通知：            注解      时机                  @Before      方法前              @AfterReturning      正常返回后              @AfterThrowing      抛异常后              @After      finally 语义              @Around      包裹方法，可控制是否执行      5.2 第一个切面@Aspect@Componentpublic class LoggingAspect {    private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);    @Pointcut("within(com.example.order.service..*)")    public void orderServiceMethods() {}    @Around("orderServiceMethods()")    public Object logCost(ProceedingJoinPoint pjp) throws Throwable {        long start = System.nanoTime();        try {            return pjp.proceed();        } finally {            long cost = (System.nanoTime() - start) / 1_000_000;            log.info("{} cost={}ms", pjp.getSignature().toShortString(), cost);        }    }}依赖：&lt;dependency&gt;    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;    &lt;artifactId&gt;spring-boot-starter-aop&lt;/artifactId&gt;&lt;/dependency&gt;5.3 切点表达式execution(* com.example.order.service.OrderService.create(..))within(com.example.order.service..*)@annotation(com.example.log.LogCost)@within(com.example.log.LogModule)bean(orderService)target(com.example.order.service.OrderService)常用表达式：            表达式      说明                  execution      按方法签名匹配              within      按类型包范围匹配              @annotation      按方法注解匹配              @within      按类注解匹配              bean      按 Bean 名称匹配              args      按参数类型匹配      工程建议：避免复杂通用表达式，优先使用注解切点，可读性更好。5.4 自定义注解切面注解：@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface LogCost {    String value() default "";}切面：@Aspect@Componentpublic class LogCostAspect {    @Around("@annotation(logCost)")    public Object around(ProceedingJoinPoint pjp, LogCost logCost) throws Throwable {        long start = System.currentTimeMillis();        try {            return pjp.proceed();        } finally {            System.out.printf("%s cost %dms%n",                    logCost.value(), System.currentTimeMillis() - start);        }    }}使用：@LogCost("createOrder")public Order create(CreateOrderCommand command) {    return repository.save(new Order(command.userId()));}5.5 JDK 动态代理与 CGLIB            代理      条件      特点                  JDK 动态代理      目标有接口      基于接口反射              CGLIB      无接口或强制使用      生成子类      Spring Boot 默认通常使用 CGLIB 代理。CGLIB 限制：  final 类不能代理；  private 方法不能拦截；  final 方法不能覆盖；  构造器不能是私有或不可访问；  代理会引入类型差异；  自调用问题依然存在。5.6 自调用失效@Servicepublic class OrderService {    public void create(Order order) {        this.validate(order);    }    @LogCost("validate")    public void validate(Order order) {    }}外部调用：Proxy.create()  -&gt; target.create()     -&gt; this.validate()  // 直接调用目标对象this.validate() 绕过代理，所以切面不执行。解决方式：  拆分 Bean；  通过 AopContext.currentProxy()；  自注入代理对象；  改成外部调用；  使用 AspectJ 编译期或类加载期织入。最推荐第 1 种：把被拦截动作放到另一个职责清晰的 Bean。5.7 参数与返回值读取参数：Object[] args = pjp.getArgs();记录安全日志时必须脱敏：Object[] args = pjp.getArgs();for (Object arg : args) {    if (arg instanceof LoginCommand command) {        log.info("user={}", command.username());    }}修改返回值：@Around("...")public Object hidePhone(ProceedingJoinPoint pjp) throws Throwable {    Object result = pjp.proceed();    if (result instanceof UserView view) {        return new UserView(view.id(), mask(view.phone()));    }    return result;}不要在切面中随意吞异常：try {    return pjp.proceed();} catch (Exception e) {    return null; // 破坏调用方错误处理}5.8 切面顺序@Aspect@Order(1)public class SecurityAspect {}@Aspect@Order(2)public class LoggingAspect {}简化规则：order 小的 @Around 先进入order 小的 @After 后执行常见顺序：审计 / 权限  -&gt; 限流     -&gt; 日志        -&gt; 事务           -&gt; 业务事务通常在最内层，确保日志先记录请求，但具体顺序要按业务语义设计并写测试验证。5.9 AOP 与事务的关系@Transactional 本质是 AOP 拦截：TransactionInterceptor  -&gt; 开启事务  -&gt; 执行业务方法  -&gt; 正常提交  -&gt; 异常回滚因此以下情况会失效：  同类自调用；  方法不是 public；  final 或 static 方法；  异常被内部吞掉；  默认回滚规则不匹配受检异常；  多线程内部逻辑不在同一事务上下文；  数据库引擎不支持事务；  传播行为设置错误。5.10 性能与治理切面不是免费能力：  反射获取注解有成本；  过宽切点会拦截大量方法；  同步日志可能拖慢请求；  代理对象创建增加启动成本；  复杂序列化可能泄露敏感数据。规范：  切点必须精确；  高频路径加开关；  日志异步化；  参数脱敏；  异常只观察不吞；  给切面写单元测试；  使用 Micrometer 而不是手写指标协议；  不在切面中写业务规则。本章小结Spring AOP 通过运行时代理实现横切逻辑，核心是切点、通知和代理对象。CGLIB 与 JDK 动态代理各有条件，private、final 和自调用都会导致拦截失效。工程上应优先使用自定义注解切点，保持切面职责单一，并通过测试验证顺序和异常行为。思考题  @Around 和 @After 的执行顺序关系是什么？  为什么自调用不经过代理？  CGLIB 代理有哪些限制？  @Transactional 为什么本质上也是 AOP？  如何设计一个安全且高性能的日志切面？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。依赖注入看似只是几个注解，实际决定了代码的可测试性、可维护性和运行行为。本章比较常见注入方式，并给出工程选择建议。4.1 构造器注入推荐方式：@Servicepublic class OrderService {    private final OrderRepository repository;    private final InventoryClient inventoryClient;    private final ApplicationEventPublisher eventPublisher;    public OrderService(OrderRepository repository,                        InventoryClient inventoryClient,                        ApplicationEventPublisher eventPublisher) {        this.repository = repository;        this.inventoryClient = inventoryClient;        this.eventPublisher = eventPublisher;    }}优点：  依赖不可变；  必需依赖一目了然；  单元测试可直接 new；  容易发现类职责过多；  避免容器 API 侵入业务。当构造器参数过多时，通常不是注入方式的问题，而是类承担了太多职责。4.2 字段注入@Servicepublic class OrderService {    @Autowired    private OrderRepository repository;}问题：  依赖无法声明为 final；  不看注解难以发现依赖；  测试需要反射或容器；  隐藏循环依赖和过度耦合；  Spring 官方也不推荐作为常规方式。它适合快速示例，不适合作为团队规范。4.3 Setter 注入@Servicepublic class ReportService {    private ReportRenderer renderer;    @Autowired    public void setRenderer(ReportRenderer renderer) {        this.renderer = renderer;    }}适合可选依赖或需要运行时重新配置的场景。缺点是对象可能处于不完整状态，调用方需要处理依赖为 null 的情况。业务服务的必需依赖应优先走构造器。4.4 多候选 Bean场景：OrderRepository 接口  |-- MysqlOrderRepository  |-- MongoOrderRepository注入时报错：expected single matching bean but found 2解决方式一：@Primary@Primary@Repositorypublic class MysqlOrderRepository implements OrderRepository {}解决方式二：@Qualifierpublic OrderService(        @Qualifier("mongoOrderRepository") OrderRepository repository) {    this.repository = repository;}解决方式三：按名称注入。public OrderService(        @Autowired(required = false) Map&lt;String, Handler&gt; handlers) {    this.handlers = handlers;}策略模式常用 Map 或 List 注入：public DiscountService(Map&lt;String, DiscountStrategy&gt; strategies) {    this.strategies = strategies;}4.5 泛型注入Spring 可以利用泛型元数据选择 Bean：public interface Converter&lt;S, T&gt; {    T convert(S source);}@Componentpublic class StringToOrderConverter implements Converter&lt;String, Order&gt; {}@Componentpublic class StringToUserConverter implements Converter&lt;String, User&gt; {}注入：private final Converter&lt;String, Order&gt; orderConverter;注意泛型擦除和代理对解析的影响，复杂场景应显式命名。4.6 Optional 与 ObjectProvider可选依赖：public AuditService(Optional&lt;AuditClient&gt; client) {    this.client = client.orElse(null);}延迟或条件获取：public AuditService(ObjectProvider&lt;AuditClient&gt; clientProvider) {    this.clientProvider = clientProvider;}public void audit(OrderEvent event) {    AuditClient client = clientProvider.getIfAvailable();    if (client != null) {        client.send(event);    }}区别：            方式      特点                  Optional      注入时解析一次              ObjectProvider      延迟解析，适合条件依赖和多候选              @Autowired(required=false)      传统可选注入              @Nullable      显式允许为空      不要用 Optional 掩盖所有依赖问题。如果依赖是必需的，直接构造器注入更清晰。4.7 循环依赖设计问题@Servicepublic class OrderService {    private final InventoryService inventoryService;}@Servicepublic class InventoryService {    private final OrderService orderService;}更常见的设计味道：  两个服务彼此查询对方状态；  一个类同时负责流程编排和细节执行；  查询逻辑散落在多个服务；  缺少领域事件；  模块边界没有定义清楚。重构方式：@Servicepublic class OrderFacade {    private final OrderService orderService;    private final InventoryService inventoryService;}或者：eventPublisher.publishEvent(new OrderCreatedEvent(orderId));依赖方向应表达业务责任，而不是为了让代码编译通过。4.8 配置注入不推荐：@Value("${pay.url}")private String payUrl;大量散落的 @Value 会导致配置不可见、难以校验。推荐：@ConfigurationProperties(prefix = "pay")public record PayProperties(        String url,        Duration timeout,        int retryTimes) {}启用：@ConfigurationPropertiesScan@SpringBootApplicationpublic class Application {}配置类可用 JSR-303 校验：@ConfigurationProperties(prefix = "pay")@Validatedpublic record PayProperties(        @NotBlank String url,        @NotNull Duration timeout,        @Min(0) @Max(5) int retryTimes) {}4.9 测试中的注入单元测试优先直接构造：class OrderServiceTest {    @Test    void should_create_order() {        OrderRepository repository = mock(OrderRepository.class);        InventoryClient client = mock(InventoryClient.class);        OrderService service = new OrderService(repository, client);    }}切片测试：@WebMvcTest(OrderController.class)class OrderControllerTest {    @Autowired    MockMvc mockMvc;    @MockBean    OrderService orderService;}Spring Boot 3.4 后测试相关注解有演进，使用当前版本的推荐注解即可。4.10 团队规范建议  业务服务一律构造器注入；  必需依赖不使用 Optional；  多实现显式命名；  策略集合可用 Map/List；  配置使用 @ConfigurationProperties；  禁止字段注入；  构造器超过 7 个依赖时强制重构评审；  循环依赖不允许通过属性绕过；  基础设施组件才使用 Aware；  测试优先直接构造依赖。本章小结构造器注入是最推荐的依赖注入方式，它能暴露必需依赖、保持对象不可变并提升可测试性。字段注入适合演示，不适合生产代码。多候选 Bean 通过 @Primary、@Qualifier 或集合注入处理；配置类应集中绑定并校验。出现循环依赖时，优先重构职责和依赖方向。思考题  为什么构造器注入更容易发现类的职责膨胀？  Optional 和 ObjectProvider 分别适合什么场景？  注入 Map&lt;String, Handler&gt; 时 key 是什么？  如何避免配置散落各处？  循环依赖的正面解法有哪些？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。Bean 生命周期描述一个对象从 BeanDefinition 到可使用、再到销毁的完整过程。事务、异步、配置绑定、初始化校验、动态代理都发生在生命周期不同阶段。3.1 总体流程BeanDefinition 加载  -&gt; 实例化前处理  -&gt; 构造对象  -&gt; 属性填充  -&gt; Aware 回调  -&gt; BeanPostProcessor.before  -&gt; 初始化回调  -&gt; BeanPostProcessor.after  -&gt; Bean 就绪  -&gt; 容器关闭  -&gt; 销毁回调Spring 实际执行细节较复杂，但这条主线非常稳定。3.2 实例化常用方式：  反射调用构造器；  工厂方法实例化；  Supplier 实例化；  FactoryBean.getObject()。构造器选择：@Servicepublic class OrderService {    private final OrderRepository repository;    public OrderService(OrderRepository repository) {        this.repository = repository;    }}如果只有一个构造器，Spring 通常可直接使用。多个构造器时：  可用 @Autowired 标注首选；  可用 @ConstructorProperties 配合；  应尽量保持构造器清晰。3.3 属性填充实例化后，Spring 会注入依赖和配置值。@Componentpublic class PayClient {    @Value("${pay.timeout:3s}")    private Duration timeout;}更推荐构造器绑定：@Componentpublic class PayClient {    private final Duration timeout;    public PayClient(@Value("${pay.timeout:3s}") Duration timeout) {        this.timeout = timeout;    }}属性填充阶段也可能触发类型转换、SpEL 求值和依赖解析失败。3.4 Aware 回调常用 Aware：            接口      获得内容                  BeanNameAware      Bean 名称              BeanClassLoaderAware      类加载器              BeanFactoryAware      BeanFactory              EnvironmentAware      Environment              ApplicationContextAware      ApplicationContext      示例：@Componentpublic class ContextHolder implements ApplicationContextAware {    private ApplicationContext context;    @Override    public void setApplicationContext(ApplicationContext context) {        this.context = context;    }}Aware 会把容器对象带进业务代码，增加耦合。非框架组件应优先使用构造器注入；只有通用基础设施才适合使用 Aware。3.5 初始化回调三种常见方式：@Componentpublic class CacheLoader implements InitializingBean {    @Override    public void afterPropertiesSet() {        reload();    }}JSR-250：@PostConstructpublic void init() {    reload();}声明式：@Bean(initMethod = "start", destroyMethod = "close")public StorageClient storageClient() {    return new StorageClient();}推荐顺序：  业务 Bean 优先 @PostConstruct，简单直观；  第三方对象优先 @Bean(initMethod, destroyMethod)，不侵入库代码；  框架内部才实现 InitializingBean。3.6 BeanPostProcessorBeanPostProcessor 在初始化前后介入：@Componentpublic class AuditBeanPostProcessor implements BeanPostProcessor {    @Override    public Object postProcessAfterInitialization(Object bean, String beanName) {        if (bean instanceof Auditable auditable) {            return Proxy.newProxyInstance(                    bean.getClass().getClassLoader(),                    bean.getClass().getInterfaces(),                    (proxy, method, args) -&gt; {                        System.out.println("before " + method.getName());                        return method.invoke(bean, args);                    });        }        return bean;    }}典型内置处理器：            处理器      作用                  AutowiredAnnotationBeanPostProcessor      解析 @Autowired              CommonAnnotationBeanPostProcessor      @PostConstruct、@PreDestroy、资源注入              ApplicationContextAwareProcessor      Aware 回调              AsyncAnnotationBeanPostProcessor      @Async 代理              AbstractAutoProxyCreator      AOP 代理      BeanPostProcessor 会影响所有 Bean，应保持轻量、稳定。3.7 销毁回调容器关闭时：发布 ContextClosedEvent  -&gt; 销毁单例  -&gt; @PreDestroy  -&gt; DisposableBean.destroy()  -&gt; @Bean(destroyMethod)示例：@PreDestroypublic void shutdown() {    scheduler.shutdown();}Spring Boot 收到 SIGTERM 后会关闭上下文。若 Java 进程没有收到信号，例如启动脚本没有 exec，销毁回调不会执行。3.8 作用域            作用域      生命周期                  singleton      容器内一个实例              prototype      每次获取创建新实例              request      HTTP 请求内              session      HTTP 会话内              application      ServletContext 内              websocket      WebSocket 会话内      singleton Bean 注入 prototype Bean 时，只会注入一次。需要每次新对象时可用：@Componentpublic class TaskFactory {    private final ObjectProvider&lt;TaskWorker&gt; workers;    public TaskFactory(ObjectProvider&lt;TaskWorker&gt; workers) {        this.workers = workers;    }    public TaskWorker newWorker() {        return workers.getObject();    }}3.9 代理对生命周期的影响如果 Bean 需要 AOP，容器最终放入单例池的可能是代理对象。目标对象  -&gt; 属性填充  -&gt; 初始化  -&gt; AbstractAutoProxyCreator  -&gt; 代理对象  -&gt; 容器对外暴露代理常见影响：  注入类型可能是代理类型；  @Transactional 自调用失效；  final 类或方法代理失败；  CGLIB 代理需要可访问构造器；  在构造器中使用依赖可能拿到 null。3.10 初始化中的阻塞问题错误做法：@PostConstructpublic void init() {    // 远程加载大配置，可能耗时 30s    remoteConfig.loadAll();}后果：  应用启动慢；  健康检查误判；  发布滚动阻塞；  远程故障导致启动失败。建议：  启动只加载必要数据；  异步预热后台执行；  使用就绪探针区分启动和可服务；  外部调用设置超时和重试；  首次加载失败允许降级。3.11 调试生命周期开启 Bean 创建日志：logging.level.org.springframework.beans.factory=DEBUGlogging.level.org.springframework.context=DEBUG断点位置：            问题      断点                  Bean 未创建      AbstractBeanFactory.getBean              依赖注入失败      AbstractAutowireCapableBeanFactory.populateBean              初始化失败      initializeBean              AOP 没生效      AbstractAutoProxyCreator              循环依赖      DefaultSingletonBeanRegistry      也可以自定义处理器打印关键阶段：@Componentpublic class BeanLifecycleLogger implements BeanPostProcessor {    @Override    public Object postProcessBeforeInitialization(Object bean, String beanName) {        if (beanName.startsWith("order")) {            System.out.println("before init: " + beanName);        }        return bean;    }}本章小结Bean 生命周期由实例化、属性填充、Aware 回调、初始化前后处理、初始化方法、代理生成和销毁回调组成。BeanPostProcessor 是 Spring 扩展体系的核心，也是自动注入和 AOP 的实现基础。业务代码应优先使用清晰的构造器注入和 @PostConstruct，避免初始化阶段做长时间阻塞操作。思考题  @PostConstruct 和 afterPropertiesSet 谁先执行？  BeanPostProcessor 能否修改 BeanDefinition？  singleton 注入 prototype 为什么只创建一次？  为什么初始化方法中不适合调用慢速远程服务？  AOP 代理是在生命周期哪个阶段生成的？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。IoC 容器是 Spring 的地基。它负责加载 Bean 定义、实例化对象、注入依赖、处理生命周期，并在合适时机销毁对象。理解容器的工作方式，后面的事务失效、循环依赖、条件装配、作用域问题都会变得容易解释。2.1 容器的基本职责BeanDefinition  -&gt; BeanFactory     -&gt; 实例化     -&gt; 属性填充     -&gt; 初始化     -&gt; 放入单例池     -&gt; 销毁回调核心职责：            职责      说明                  注册 Bean 定义      注解、XML、Java Config 都会转成 BeanDefinition              实例化 Bean      反射或工厂方法创建对象              依赖注入      构造器、字段、setter 等方式              生命周期回调      Aware、BeanPostProcessor、InitializingBean 等              作用域管理      singleton、prototype、request、session 等              事件发布      ApplicationEvent 与监听器      2.2 BeanFactory 与 ApplicationContextBeanFactory 提供基础容器能力，ApplicationContext 在其上增加了：  资源加载；  环境抽象；  事件机制；  国际化；  注解驱动；  自动后处理器注册。常见实现：            容器      常见场景                  AnnotationConfigApplicationContext      普通 Java 配置              AnnotationConfigServletWebApplicationContext      Servlet Web              GenericWebApplicationContext      通用 Web 容器              ReactiveWebServerApplicationContext      Reactive 应用      Spring Boot 不会让开发者手工 new 容器，而是通过 SpringApplication.run 创建并刷新合适类型的 ApplicationContext。2.3 BeanDefinitionBeanDefinition 是 Spring 的“对象图纸”，记录：beanClassNamescopelazyInitprimarydependsOnautowireCandidatefactoryBeanNamefactoryMethodNameinitMethodNamedestroyMethodNamepropertyValuesconstructorArgumentValues不同来源会被统一解析：            来源      解析方式                  @Component      ClassPathScanningCandidateComponentProvider 扫描              @Bean      ConfigurationClassPostProcessor 解析              XML      XmlBeanDefinitionReader              ImportBeanDefinitionRegistrar      编程式注册              BeanDefinitionRegistryPostProcessor      容器早期扩展      这也是 MyBatis、Dubbo 等框架能与 Spring 集成的基础。2.4 refresh 流程AbstractApplicationContext.refresh() 是容器启动主线：1. prepareRefresh2. obtainFreshBeanFactory3. prepareBeanFactory4. postProcessBeanFactory5. invokeBeanFactoryPostProcessors6. registerBeanPostProcessors7. initMessageSource8. initApplicationEventMulticaster9. onRefresh10. registerListeners11. finishBeanFactoryInitialization12. finishRefresh关键阶段：            阶段      作用                  invokeBeanFactoryPostProcessors      修改或补充 BeanDefinition              registerBeanPostProcessors      注册前后处理器              onRefresh      Web 容器创建内嵌服务器              finishBeanFactoryInitialization      初始化非懒加载单例              finishRefresh      发布容器就绪事件      Spring Boot 3.x 的内部实现仍有调整，但主线思想稳定。2.5 手写一个最小容器为了去掉魔法，可以手写一个极简版 IoC：class Container {    private final Map&lt;String, Object&gt; singletons = new ConcurrentHashMap&lt;&gt;();    private final Map&lt;String, Class&lt;?&gt;&gt; definitions = new ConcurrentHashMap&lt;&gt;();    void register(Class&lt;?&gt; type) {        definitions.put(type.getName(), type);    }    synchronized Object getBean(Class&lt;?&gt; type) throws Exception {        String name = type.getName();        if (singletons.containsKey(name)) {            return singletons.get(name);        }        Class&lt;?&gt; impl = definitions.get(name);        Object bean = impl.getDeclaredConstructor().newInstance();        singletons.put(name, bean);        return bean;    }}Spring 的真实容器还必须处理：  接口到实现的候选关系；  构造参数解析；  循环依赖；  作用域；  代理；  生命周期回调；  条件装配；  线程安全；  FactoryBean；  类加载器。2.6 Bean 名称与别名默认命名规则：            定义方式      默认名                  @Component      类名首字母小写              @Component("orderService")      显式指定              @Bean      方法名              @Bean("mapper")      显式指定              XML id      显式指定      特殊情况：如果类名前两个字母都是大写，名称可能保持原类名。不要依赖这类边界规则，重要 Bean 应显式命名。别名示例：@Bean(name = {"objectMapper", "jsonMapper"})public ObjectMapper objectMapper() {    return new ObjectMapper();}2.7 条件装配常用条件注解：            注解      含义                  @ConditionalOnClass      类存在              @ConditionalOnMissingBean      容器中没有指定 Bean              @ConditionalOnProperty      属性匹配              @ConditionalOnBean      指定 Bean 存在              @ConditionalOnWebApplication      是 Web 应用              @Profile      环境匹配      示例：@Configurationpublic class IdGeneratorConfig {    @Bean    @ConditionalOnMissingBean(IdGenerator.class)    public IdGenerator uuidGenerator() {        return UUID::randomUUID;    }}这类代码常见于 Starter：框架给默认实现，业务覆盖后默认实现退出。2.8 FactoryBeanFactoryBean 是创建 Bean 的工厂对象。@Componentpublic class PaymentClientFactoryBean implements FactoryBean&lt;PaymentClient&gt; {    @Override    public PaymentClient getObject() {        return new PaymentClient("https://payment.example.com");    }    @Override    public Class&lt;?&gt; getObjectType() {        return PaymentClient.class;    }}注入的是 getObject() 返回的对象；如果想拿工厂本身，需要在名称前加 &amp;：Object factory = applicationContext.getBean("&amp;paymentClientFactoryBean");典型用途：代理对象、第三方客户端、复杂构建流程。2.9 循环依赖典型场景：A 依赖 BB 依赖 ASpring 对“单例 + setter/字段注入”的三级缓存处理：            缓存      存放内容                  singletonObjects      完整 Bean              earlySingletonObjects      提前暴露的早期 Bean              singletonFactories      可以生成早期引用的工厂      流程：创建 A  -&gt; 提前暴露 A 的工厂  -&gt; 注入 B     -&gt; 创建 B     -&gt; B 注入 A 的早期引用     -&gt; B 完成  -&gt; A 完成限制：  构造器循环依赖无法通过该机制解决；  prototype 作用域不适用；  如果 Bean 需要 AOP，提前暴露的可能是代理引用；  Spring Boot 2.6+ 默认禁止循环依赖；  循环依赖通常说明职责边界不清晰。更好的修复：  抽出第三个服务；  使用事件解耦；  延迟调用；  重新划分模块；  用 ObjectProvider 只在必要场景延迟获取。2.10 常见问题排查            异常      常见原因                  NoSuchBeanDefinitionException      未扫描、条件不满足、名字不匹配              NoUniqueBeanDefinitionException      多个候选缺少 @Primary 或 @Qualifier              BeanCurrentlyInCreationException      循环依赖              UnsatisfiedDependencyException      缺少依赖或构造器无法解析              BeanDefinitionStoreException      配置类或扫描路径错误              BeanIsAbstractException      注入抽象 Bean      排查命令：applicationContext.containsBean("orderService");Arrays.toString(applicationContext.getBeanDefinitionNames());applicationContext.getBeansOfType(OrderRepository.class);Actuator 也可查看：/actuator/beans生产环境应谨慎暴露该端点，因为它会暴露内部结构。本章小结IoC 容器以 BeanDefinition 为输入，经过容器刷新、BeanFactoryPostProcessor、BeanPostProcessor 和单例初始化，把对象组装成可用应用。BeanFactory 是基础容器，ApplicationContext 是面向应用的增强实现。理解 refresh 主线、条件装配、FactoryBean 和循环依赖边界，是从会用 Spring 到理解 Spring 的关键一步。思考题  BeanDefinition 和 Bean 实例有什么区别？  BeanFactoryPostProcessor 和 BeanPostProcessor 的执行时机有何不同？  为什么 Spring Boot 2.6+ 默认禁止循环依赖？  FactoryBean 和普通 Bean 有什么区别？  遇到 NoSuchBeanDefinitionException 时如何排查？</li>
  <li>这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。Spring 是 Java 企业级开发的事实标准框架。它解决的核心问题是：把对象创建、依赖组装、事务边界、配置管理、远程调用和通用工程问题，从业务代码中抽离出来，交给容器和框架处理。Spring Boot 让 Spring 更容易启动和部署，Spring Cloud 让微服务治理有了一套可组合的生态。理解 Spring 的关键不是背注解，而是理解容器、Bean 生命周期、扩展点、自动装配和微服务治理边界。1.1 没有 Spring 时如何写程序手工组装对象：public class OrderService {    private final OrderRepository repository;    private final InventoryClient inventoryClient;    private final PaymentClient paymentClient;    public OrderService(OrderRepository repository,                        InventoryClient inventoryClient,                        PaymentClient paymentClient) {        this.repository = repository;        this.inventoryClient = inventoryClient;        this.paymentClient = paymentClient;    }}手动创建：DataSource dataSource = new HikariDataSource(config);OrderRepository repository = new JdbcOrderRepository(dataSource);InventoryClient inventoryClient = new InventoryHttpClient(client);PaymentClient paymentClient = new PaymentGrpcClient(channel);OrderService service = new OrderService(repository, inventoryClient, paymentClient);问题：  对象创建逻辑分散；  依赖关系手工维护；  事务、监控、代理等通用能力重复实现；  测试替换困难；  生命周期难以统一管理。Spring 的答案是把对象交给 IoC 容器统一管理。1.2 IoC 与 DIIoC（Inversion of Control）控制反转，指对象创建和依赖组装的控制权从应用代码转移到容器。DI（Dependency Injection）依赖注入，是实现 IoC 的主要方式。@Servicepublic class OrderService {    private final OrderRepository repository;    private final InventoryClient inventoryClient;    public OrderService(OrderRepository repository,                        InventoryClient inventoryClient) {        this.repository = repository;        this.inventoryClient = inventoryClient;    }}Spring 负责：  扫描 Bean 定义；  构造对象；  注入依赖；  初始化回调；  管理作用域；  销毁回调。1.3 Bean 是什么Bean 是由 Spring 容器管理的对象。常见定义方式：@Configurationpublic class AppConfig {    @Bean    public ObjectMapper objectMapper() {        return new ObjectMapper()                .registerModule(new JavaTimeModule())                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);    }}常见注解：            注解      说明                  @Component      通用组件              @Service      业务服务              @Repository      数据访问              @Controller      Web 控制器              @Configuration      配置类              @Bean      方法级 Bean 定义              @Autowired      注入依赖              @Qualifier      指定候选 Bean              @Primary      优先候选              @Value      注入配置              @Scope      作用域      推荐使用构造器注入，原因：  依赖不可变；  必需依赖明确；  便于单元测试；  避免字段注入的隐藏依赖；  与 final 字段配合更好。1.4 Spring 的模块全景Spring Framework  |-- Core / Beans / Context  |-- AOP  |-- Transaction  |-- JDBC / ORM  |-- Web / WebMvc  |-- Messaging  +-- TestSpring Boot  |-- Auto Configuration  |-- Starter  |-- Actuator  |-- Embedded Server  +-- Production UtilitiesSpring Cloud  |-- Discovery  |-- Config  |-- Gateway  |-- Circuit Breaker  |-- Load Balancer  |-- Tracing  +-- Stream / Bus            模块      解决问题                  Core Container      Bean 管理、依赖注入、事件              AOP      横切逻辑模块化              Transaction      声明式事务              Spring MVC      Web 与 REST              Spring Data      数据访问抽象              Spring Boot      自动装配与工程简化              Spring Cloud      微服务治理      1.5 AOPAOP（面向切面编程）把日志、事务、权限、监控、限流这类横切逻辑从业务方法中抽离。概念：            概念      说明                  Joinpoint      程序执行点，Spring 中通常是方法              Pointcut      哪些方法被拦截              Advice      拦截后执行的动作              Aspect      切面，Pointcut + Advice              Target      被代理对象              Proxy      代理对象      示例：@Aspect@Componentpublic class LoggingAspect {    @Around("execution(* com.example.order.service..*(..))")    public Object around(ProceedingJoinPoint pjp) throws Throwable {        long start = System.currentTimeMillis();        try {            return pjp.proceed();        } finally {            long cost = System.currentTimeMillis() - start;            System.out.printf("%s cost %dms%n", pjp.getSignature(), cost);        }    }}注意：Spring AOP 默认基于代理，因此：  同类内部调用不会经过代理；  private 方法不能被拦截；  final 方法可能无法代理；  代理对象和目标对象类型不同；  自调用事务失效是常见问题。1.6 Spring Boot 的价值没有 Spring Boot 时，需要：  手写大量 XML 或配置类；  引入兼容版本依赖；  配置 DispatcherServlet；  配置数据源和事务；  打 WAR 部署外部 Tomcat；  自建健康检查和监控端点。Spring Boot 提供：@SpringBootApplicationpublic class OrderApplication {    public static void main(String[] args) {        SpringApplication.run(OrderApplication.class, args);    }}核心能力：            能力      说明                  Starter      依赖聚合和版本管理              Auto Configuration      按条件自动装配              Externalized Config      多环境配置              Embedded Server      内嵌 Tomcat/Jetty/Undertow              Actuator      健康检查和指标              Dev Tools      开发效率工具              Test      测试支持      1.7 Spring Cloud 微服务全景单体架构：Client -&gt; Monolith -&gt; Database微服务架构：Client  -&gt; Gateway     -&gt; Order Service -&gt; MySQL     -&gt; Product Service -&gt; MySQL     -&gt; Inventory Service -&gt; Redis     -&gt; Search Service -&gt; ElasticsearchSpring Cloud 常见组件：            问题      常用方案                  服务注册发现      Spring Cloud Netflix Eureka / Consul / Nacos              配置管理      Spring Cloud Config / Nacos              网关      Spring Cloud Gateway              负载均衡      Spring Cloud LoadBalancer              熔断限流      Resilience4j / Sentinel              链路追踪      Micrometer Tracing / OpenTelemetry              消息      Spring Cloud Stream / Kafka / RocketMQ      微服务不是银弹，它同时带来：  分布式事务；  服务治理；  链路追踪；  发布编排；  数据一致性；  排障复杂度。1.8 一个典型请求链路HTTP Request  -&gt; Gateway     -&gt; Filter     -&gt; Load Balancer     -&gt; Order Controller     -&gt; Order Service        -&gt; Transaction Proxy        -&gt; Repository        -&gt; MySQL     -&gt; Kafka Producer  -&gt; HTTP Response在这条链路中，Spring 提供的能力包括：  URL 路由；  参数绑定；  校验；  权限拦截；  事务；  异常转换；  远程调用；  监控埋点。1.9 学习 Spring 的路线第一阶段：使用  Bean 注入；  REST 接口；  配置文件；  数据访问；  事务；  测试。第二阶段：理解  容器启动；  Bean 生命周期；  扩展点；  代理；  自动装配；  配置加载优先级。第三阶段：工程化  统一异常；  日志规范；  链路追踪；  安全；  参数校验；  性能调优。第四阶段：微服务治理  注册发现；  网关；  熔断降级；  分布式事务；  事件驱动；  服务网格边界。本章小结Spring 的核心是 IoC 容器和 AOP 代理。Spring Boot 通过 Starter 和自动装配降低工程成本，Spring Cloud 提供微服务治理组件。学习 Spring 要从 Bean 和依赖关系开始，再到事务与代理机制，最后进入自动装配、启动流程和生产治理。思考题  IoC 和 DI 的关系是什么？  为什么推荐构造器注入？  Spring AOP 为什么会导致自调用事务失效？  Spring Boot 自动装配解决了什么问题？  微服务化后，系统复杂度新增在哪里？</li>
</ul>
