Skip to content

Spring Boot 从零到精通验收清单 ​

这页不是“背几个注解”的清单,而是用来验收你是否真正理解 Spring Boot:为什么它能快速启动,自动配置到底怎么生效,Starter 为什么能开箱即用,扩展点应该放在哪个生命周期阶段,线上出了问题怎么定位。

学习 Spring Boot 的核心目标不是会写 @RestController,而是能回答下面这些问题:

  • 一个应用从 main 方法到端口可访问,中间发生了什么。
  • 一个默认 Bean 为什么有时会出现,有时又被你自己的 Bean 替换。
  • 配置文件、环境变量、命令行参数、配置中心最终谁生效。
  • 内嵌 Tomcat 什么时候创建,HTTP 请求如何进入 Controller。
  • 扩展点应该怎么选,选错会导致什么问题。
  • 商业系统里如何把公共客户端、监控、错误诊断沉淀成可复用 Starter。

最终目标 ​

学完本页和关联知识点,你应该能做到:

能力达标标准
会用能独立搭建 REST 服务,接入数据库、Redis、MQ、配置、日志、监控
懂原理能画出启动流程、自动配置流程、请求链路、配置绑定链路
会扩展能选择合适扩展点解决配置解密、默认 Bean、健康检查、指标、失败诊断
能排查能通过 conditions、env、configprops、metrics、日志、线程栈定位问题
能面试能把标准回答和底层原理、项目落地、失败边界串起来

总路线 ​

mermaid
flowchart TD
    A["认识 Spring Boot 的工程化价值"] --> B["理解 SpringApplication.run 启动主线"]
    B --> C["理解配置加载与 Environment"]
    C --> D["理解自动配置候选类如何导入"]
    D --> E["理解条件注解如何决定 Bean 是否注册"]
    E --> F["理解 refresh 创建 Bean 和内嵌 Tomcat"]
    F --> G["理解一次 HTTP 请求链路"]
    G --> H["掌握扩展点选择和生产排查"]

阶段 1:Spring Boot 到底解决什么问题 ​

是什么 ​

Spring Boot 是基于 Spring 的应用工程化框架。它不替代 Spring IOC、AOP、事务、MVC,而是把常用工程能力封装成约定:依赖版本、自动配置、内嵌容器、外部化配置、监控端点、打包运行。

传统 Spring 项目经常需要手动做这些事:

  • 写大量 XML 或 Java Config。
  • 自己处理依赖版本兼容。
  • 手动部署到外部 Tomcat。
  • 每个项目重复配置数据源、MVC、JSON、事务、日志。
  • 线上缺少统一健康检查和指标。

Spring Boot 把这些重复工作做成默认能力,让业务项目只关注业务差异。

为什么需要 ​

商业项目中,一个订单服务、支付服务、采集服务、库存服务的基础能力高度相似:HTTP 接口、参数校验、数据库连接池、Redis、日志、监控、配置、多环境、优雅停机。如果每个项目都手写,容易出现三个问题:

问题后果
配置重复每个项目风格不同,出问题难排查
版本混乱依赖冲突、运行时报错、升级困难
缺少默认治理没有健康检查、指标、统一异常、配置排查入口

Spring Boot 的价值就是把“基础设施默认做好”,把“业务差异留给项目覆盖”。

不这样会怎样 ​

如果没有 Spring Boot 或类似工程化框架,项目早期看起来灵活,后期会出现:

  • 新服务创建慢,每次复制粘贴旧工程。
  • XML、配置类、依赖版本散落各处。
  • 线上启动失败时不知道哪个自动配置或 Bean 出错。
  • 公共能力无法沉淀成 Starter,只能靠文档约定。
  • 运维平台无法统一通过健康检查判断实例是否可接流量。

阶段 2:启动类和 SpringApplication.run ​

最小启动类 ​

java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}

@SpringBootApplication 不是魔法,它主要组合了三个能力:

注解作用
@SpringBootConfiguration表示这是一个 Spring 配置类
@ComponentScan扫描当前包及子包下的组件
@EnableAutoConfiguration开启 Spring Boot 自动配置

启动全过程 ​

mermaid
flowchart TD
    A["main 方法"] --> B["创建 SpringApplication"]
    B --> C["推断应用类型"]
    C --> D["加载初始化器和监听器"]
    D --> E["准备 Environment"]
    E --> F["创建 ApplicationContext"]
    F --> G["解析启动类和自动配置"]
    G --> H["注册 BeanDefinition"]
    H --> I["refresh 创建 Bean"]
    I --> J["启动内嵌 Tomcat"]
    J --> K["发布启动完成事件"]
    K --> L["执行 Runner"]

关键点:

  • Environment 准备得很早,因为自动配置条件和配置绑定都要读取配置。
  • 自动配置先注册 BeanDefinition,不是马上创建所有对象。
  • 真正创建非懒加载单例 Bean 主要发生在 refresh() 阶段。
  • Servlet Web 应用的内嵌 Tomcat 在 refresh() 过程中创建和启动。
  • ApplicationRunner、CommandLineRunner 在容器基本可用后执行。

常见误区 ​

误区正确理解
启动类一运行就创建所有 Bean先准备环境、解析配置类、注册定义,后面才创建 Bean
自动配置等于直接 new 对象自动配置本质是注册配置类和 BeanDefinition
Runner 能改自动配置条件Runner 太晚,条件判断和很多 Bean 创建已经结束
端口启动发生在最后内嵌 WebServer 在 refresh() 中启动,Runner 之前通常已经可监听端口

详细原理继续看:启动流程全过程。

阶段 3:配置体系、Profile 和 Binder ​

配置加载解决什么问题 ​

同一份代码要部署到开发、测试、预发、生产,数据库地址、Redis 地址、线程池大小、日志级别、医院接口地址都不同。配置体系的作用就是把环境差异从代码里抽出来。

配置来源 ​

常见配置来源包括:

  • application.yml
  • application-dev.yml
  • 环境变量
  • JVM 参数
  • 命令行参数
  • 配置中心
  • 测试注解配置

线上排查时不能只看某一个 yml 文件,因为最终生效值可能被更高优先级的配置源覆盖。

配置绑定 Demo ​

java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@Validated
@ConfigurationProperties(prefix = "collect.client")
public class CollectClientProperties {
    private boolean enabled = true;
    private String endpoint;
    private int connectTimeoutMillis = 3000;
    private int readTimeoutMillis = 5000;

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public String getEndpoint() {
        return endpoint;
    }

    public void setEndpoint(String endpoint) {
        this.endpoint = endpoint;
    }

    public int getConnectTimeoutMillis() {
        return connectTimeoutMillis;
    }

    public void setConnectTimeoutMillis(int connectTimeoutMillis) {
        this.connectTimeoutMillis = connectTimeoutMillis;
    }

    public int getReadTimeoutMillis() {
        return readTimeoutMillis;
    }

    public void setReadTimeoutMillis(int readTimeoutMillis) {
        this.readTimeoutMillis = readTimeoutMillis;
    }
}
yaml
collect:
  client:
    enabled: true
    endpoint: https://hospital.example.com/open-api
    connect-timeout-millis: 3000
    read-timeout-millis: 5000

为什么推荐 @ConfigurationProperties,而不是到处写 @Value:

对比点@Value@ConfigurationProperties
适用场景单个简单值一组有前缀的配置
类型转换可以但分散统一绑定
嵌套结构不方便方便
校验不直观可配合校验注解
配置排查分散可通过 /actuator/configprops 查看

Binder 怎么工作 ​

mermaid
flowchart TD
    A["配置源 PropertySource"] --> B["合并到 Environment"]
    B --> C["根据 prefix 找配置项"]
    C --> D["宽松名称匹配"]
    D --> E["类型转换"]
    E --> F["填充属性对象"]
    F --> G["执行校验"]
    G --> H["注册为 Spring Bean"]

宽松名称匹配表示 connect-timeout-millis、connectTimeoutMillis、CONNECT_TIMEOUT_MILLIS 在不同来源下可以映射到同一个 Java 属性。这是为了兼容 yml、properties、环境变量等不同配置风格。

不这样会怎样 ​

如果把医院接口地址、超时时间、线程池参数写死在代码里:

  • 生产改配置必须重新发版。
  • 测试和生产容易误连。
  • 密钥可能被提交到代码仓库。
  • 自动配置条件无法基于配置开关生效。
  • 线上排查无法通过 /actuator/env、/actuator/configprops 看到最终值。

详细原理继续看:配置体系全过程。

阶段 4:自动配置全过程 ​

自动配置不是魔法 ​

自动配置可以理解为一句话:Spring Boot 根据 classpath、配置项、已有 Bean、Web 类型等条件,决定要不要给你注册一组默认 Bean。

例如业务项目引入 spring-boot-starter-web 后,classpath 中出现 Spring MVC、Tomcat、Jackson 等类。Boot 发现这些类存在,就让 Web MVC、内嵌 Tomcat、JSON 转换等自动配置参与进来。

自动配置流程 ​

mermaid
flowchart TD
    A["@SpringBootApplication"] --> B["@EnableAutoConfiguration"]
    B --> C["导入 AutoConfigurationImportSelector"]
    C --> D["读取自动配置候选类"]
    D --> E["处理 exclude 和去重排序"]
    E --> F["根据条件注解筛选"]
    F --> G["注册 BeanDefinition"]
    G --> H["refresh 阶段创建 Bean"]

Boot 2 和 Boot 3 的自动配置声明方式要分清楚:

版本常见声明文件说明
Spring Boot 2.6及更早META-INF/spring.factories通过 EnableAutoConfiguration key 声明自动配置类
Spring Boot 2.7两种机制可过渡已支持并推荐 imports,同时兼容旧候选注册方式
Spring Boot 3META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports使用每行一个自动配置类的新机制

面试时不要说“Boot 3 才有自动配置”。自动配置一直存在,只是候选类声明机制发生了变化。老项目使用 spring.factories 很常见,尤其是 Boot 2 生态的 Starter。

条件注解怎么理解 ​

条件注解含义商业项目例子
@ConditionalOnClassclasspath 中存在某个类才生效引入 HTTP 客户端 SDK 才装配采集客户端
@ConditionalOnMissingBean容器没有用户自定义 Bean 才创建默认 Bean默认提供客户端,业务可以覆盖
@ConditionalOnProperty配置开关满足才生效collect.client.enabled=true 才启用
@ConditionalOnBean已存在某个 Bean 才生效有数据源时才创建数据访问组件
@ConditionalOnWebApplicationWeb 应用才生效Web MVC 配置只在 Web 项目启用

默认 Bean Demo ​

java
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass(CollectClient.class)
@EnableConfigurationProperties(CollectClientProperties.class)
public class CollectClientAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "collect.client", name = "enabled", havingValue = "true", matchIfMissing = true)
    public CollectClient collectClient(CollectClientProperties properties) {
        return new CollectClient(
                properties.getEndpoint(),
                properties.getConnectTimeoutMillis(),
                properties.getReadTimeoutMillis()
        );
    }
}

业务项目如果不声明 CollectClient,自动配置就创建默认客户端。业务项目如果自己声明:

java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BusinessClientConfig {

    @Bean
    public CollectClient collectClient() {
        return new CollectClient("https://custom.example.com", 1000, 2000);
    }
}

因为默认配置上有 @ConditionalOnMissingBean,Boot 会让用户 Bean 优先。这就是“约定优于配置,但允许覆盖”的核心。

常见失败场景 ​

现象可能原因排查入口
自动配置类完全没出现Starter 没引入,声明文件没打进 jar依赖树、jar 内容
出现在 Negative matches条件不满足debug=true、/actuator/conditions
Positive 但 Bean 没有用户 Bean 覆盖、Bean 创建失败/actuator/beans、启动异常
配置项没生效Profile 错、被环境变量覆盖、绑定失败/actuator/env、/actuator/configprops

详细原理继续看:自动配置原理 和 自动配置与扩展点全过程。

阶段 5:Starter 机制和自定义 Starter ​

Starter 是什么 ​

Starter 是“场景依赖入口”。它本身通常不写复杂业务逻辑,而是聚合一组依赖,让业务项目引入一个依赖就获得某个场景的能力。

一个规范的自定义 Starter 通常拆成三层:

模块职责
SDK放纯 Java 客户端和核心能力,不依赖 Spring Boot
autoconfigure放自动配置类、属性类、条件注解、默认 Bean、健康检查、指标
starter只做依赖聚合,把 SDK 和 autoconfigure 带进来

为什么要拆 ​

如果把所有代码都塞到 starter 里,短期能用,长期会变成“大杂烩”:

  • SDK 无法脱离 Spring Boot 复用。
  • 自动配置逻辑无法单独测试。
  • 业务代码和基础设施耦合。
  • starter 引入后强制创建 Bean,用户无法覆盖。

商业 Demo:医疗采集客户端 Starter ​

假设多个业务服务都要调用医院采集接口,可以沉淀一个 collect-client-spring-boot-starter。

自动配置类:

java
@Configuration
@EnableConfigurationProperties(CollectClientProperties.class)
public class CollectClientAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public CollectClient collectClient(CollectClientProperties properties) {
        return new CollectClient(properties.getEndpoint());
    }

    @Bean
    public HealthIndicator collectClientHealthIndicator(CollectClient client) {
        return () -> client.ping()
                ? Health.up().withDetail("collectClient", "reachable").build()
                : Health.down().withDetail("collectClient", "unreachable").build();
    }
}

Boot 3 自动配置声明文件:

text
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
text
com.example.collect.CollectClientAutoConfiguration

业务项目只需要:

yaml
collect:
  client:
    endpoint: https://hospital.example.com/open-api

然后注入:

java
@Service
public class CollectTaskService {
    private final CollectClient collectClient;

    public CollectTaskService(CollectClient collectClient) {
        this.collectClient = collectClient;
    }

    public void pullData(String hospitalCode) {
        collectClient.pull(hospitalCode);
    }
}

这就是商业项目里 Starter 的常见价值:统一配置、统一默认实现、统一健康检查、统一指标、统一错误诊断。

详细原理继续看:Starter全过程。

阶段 6:内嵌 Tomcat 和一次 HTTP 请求链路 ​

内嵌 Tomcat 为什么能启动 ​

Spring Boot Web 项目启动时会创建 ServletWebServerApplicationContext。它在 refresh() 的 onRefresh() 阶段调用 createWebServer(),通过 ServletWebServerFactory 创建内嵌 Tomcat、Jetty 或 Undertow。

如果 classpath 中有 Tomcat,并且是 Servlet Web 应用,Boot 默认会配置 Tomcat。

请求链路 ​

mermaid
flowchart TD
    A["HTTP 请求"] --> B["Tomcat Connector"]
    B --> C["Servlet Filter 链"]
    C --> D["DispatcherServlet"]
    D --> E["HandlerMapping 找 Controller"]
    E --> F["ArgumentResolver 参数解析"]
    F --> G["参数校验"]
    G --> H["Controller"]
    H --> I["Service 业务编排"]
    I --> J["Mapper 或 Repository"]
    J --> K["数据库 Redis MQ 下游"]
    K --> L["返回业务结果"]
    L --> M["HttpMessageConverter 写 JSON"]

Filter、Interceptor、ArgumentResolver 怎么选 ​

扩展点执行位置适合场景
FilterServlet 层,进入 Spring MVC 前traceId、编码、底层安全过滤
InterceptorSpring MVC 找到 Handler 后登录态、权限、接口审计、访问日志
ArgumentResolverController 参数解析阶段自动注入当前用户、租户、请求上下文
ResponseBodyAdvice返回对象写出前统一响应包装、补充 traceId
ControllerAdviceController 异常处理统一异常和错误码

不这样会怎样 ​

如果每个 Controller 都手动解析 token、校验参数、try-catch 包异常:

  • 代码重复。
  • 不同接口返回结构不一致。
  • 权限和审计容易漏。
  • 出问题时 traceId、错误码、日志字段不统一。

所以商业项目通常把这些能力放进 Web 层扩展点,而不是散落到业务方法里。

阶段 7:扩展点选择 ​

扩展点不是越早越好,也不是越多越好。关键是你要知道自己想干预哪一个阶段。

mermaid
flowchart TD
    A["配置还没完全准备"] --> B["EnvironmentPostProcessor"]
    B --> C["Context 创建但未刷新"]
    C --> D["ApplicationContextInitializer"]
    D --> E["BeanDefinition 注册阶段"]
    E --> F["BeanDefinitionRegistryPostProcessor"]
    F --> G["修改 BeanDefinition"]
    G --> H["BeanFactoryPostProcessor"]
    H --> I["Bean 初始化前后增强"]
    I --> J["BeanPostProcessor"]
    J --> K["容器启动完成"]
    K --> L["ApplicationRunner"]
    L --> M["长期组件启动停止"]
    M --> N["SmartLifecycle"]

扩展点速查 ​

扩展点什么时候用选错后果
EnvironmentPostProcessor配置解密、提前注入配置源放到 Runner 就太晚,自动配置已经判断完
ApplicationContextInitializerrefresh() 前调整 Context不能访问完整业务 Bean
BeanDefinitionRegistryPostProcessor批量注册 BeanDefinition提前 getBean() 会导致 Bean 过早创建
BeanFactoryPostProcessor修改 BeanDefinition 属性不适合写业务逻辑
BeanPostProcessorBean 初始化前后增强可能影响 AOP、代理、生命周期
ApplicationRunner启动后轻量预热和检查放长任务会拖慢启动
SmartLifecycle管理长连接、消费者、采集器生命周期构造方法启动线程会导致关闭不可控
HealthIndicator暴露健康检查检查过重会拖慢探针
MeterBinder注册业务指标tag 基数过高会打爆监控系统
FailureAnalyzer启动失败给出友好诊断没有诊断时开发只能看长堆栈

配置解密 Demo ​

配置解密必须发生得足够早,否则数据源、Redis、自动配置条件可能已经读取了未解密值。

java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.HashMap;
import java.util.Map;

public class DecryptEnvironmentPostProcessor implements EnvironmentPostProcessor {

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        String encrypted = environment.getProperty("collect.client.secret");
        if (encrypted == null || !encrypted.startsWith("ENC(")) {
            return;
        }

        String plain = decrypt(encrypted);
        Map<String, Object> values = new HashMap<>();
        values.put("collect.client.secret", plain);
        environment.getPropertySources().addFirst(new MapPropertySource("decryptedProperties", values));
    }

    private String decrypt(String encrypted) {
        return encrypted.substring(4, encrypted.length() - 1);
    }
}

Boot 2 注册:

properties
org.springframework.boot.env.EnvironmentPostProcessor=\
com.example.boot.DecryptEnvironmentPostProcessor

Boot 3 仍可使用对应的 SPI 声明方式,实际项目要根据 Boot 版本查看官方支持的注册文件。重点不是背文件名,而是知道:配置解密必须在配置绑定和自动配置条件判断前完成。

健康检查 Demo ​

java
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

@Component
public class CollectDependencyHealthIndicator implements HealthIndicator {

    private final CollectClient collectClient;

    public CollectDependencyHealthIndicator(CollectClient collectClient) {
        this.collectClient = collectClient;
    }

    @Override
    public Health health() {
        if (collectClient.ping()) {
            return Health.up().withDetail("dependency", "collect-api").build();
        }
        return Health.down().withDetail("dependency", "collect-api").build();
    }
}

生产上要注意:健康检查不能每次都访问一个很慢的外部接口,否则 K8s 或网关频繁探测会把外部系统打爆。常见做法是使用短超时、缓存最近检查结果、区分 liveness 和 readiness。

详细原理继续看:Spring Boot 扩展点。

阶段 8:Actuator、可观测性和生产排查 ​

Actuator 是什么 ​

Actuator 是 Spring Boot 面向生产的运维端点体系。它不是业务接口,而是给开发、运维、监控平台看的诊断入口。

常见端点:

端点作用
/actuator/health健康检查
/actuator/metrics指标
/actuator/prometheusPrometheus 抓取指标
/actuator/env查看最终环境配置
/actuator/configprops查看配置绑定结果
/actuator/conditions查看自动配置条件匹配
/actuator/beans查看 Bean
/actuator/threaddump查看线程栈
/actuator/loggers动态查看或调整日志级别

生产排查总流程 ​

mermaid
flowchart TD
    A["发现问题"] --> B["确认现象和影响范围"]
    B --> C["查看日志和 traceId"]
    C --> D["看 Actuator 指标和健康状态"]
    D --> E["判断慢在应用还是下游"]
    E --> F["检查线程池连接池和 GC"]
    F --> G["检查 DB Redis MQ 外部接口"]
    G --> H["定位根因并制定修复"]

常见问题排查 ​

问题排查路径
启动失败看异常根因、conditions、配置绑定、端口占用、Bean 创建失败
自动配置没生效看 starter 依赖、自动配置声明、conditions、exclude、用户 Bean
配置不生效看 active profile、配置源优先级、env、configprops
接口慢traceId、Controller、Service、慢 SQL、Redis、MQ、下游、线程栈
内存升高heapdump、GC 日志、对象引用、缓存、大对象、线程本地变量
CPU 飙高top、线程栈、死循环、序列化、正则、频繁 GC
健康检查失败区分 liveness/readiness,检查依赖、连接池、超时策略

Actuator 安全边界 ​

生产不能随便暴露所有端点。env、configprops、beans、heapdump、threaddump 可能泄露密钥、内部类结构、线程栈、内存内容。常见做法:

  • 只对公网暴露必要的 health 或 info。
  • 管理端口走内网。
  • 敏感端点加鉴权。
  • health 细节不要直接暴露给外部。
  • 敏感配置做脱敏。

详细原理继续看:Actuator监控。

阶段 9:商业场景验收 ​

场景 1:订单服务统一 Web 能力 ​

目标:订单服务所有接口都需要统一 traceId、统一异常、统一返回、参数校验。

推荐设计:

能力放置位置
traceIdFilter
登录用户Interceptor 或 ArgumentResolver
参数校验Bean Validation
统一异常@RestControllerAdvice
统一返回ResponseBodyAdvice
接口耗时Interceptor 或 AOP

验收问题:

  • 为什么 traceId 更适合 Filter?
  • 为什么业务异常不应该在每个 Controller 里 try-catch?
  • 文件下载接口为什么要排除统一响应包装?

场景 2:医疗采集客户端 Starter ​

目标:多个服务都要调用医院采集接口,不希望每个项目重复写客户端、配置、健康检查和指标。

推荐设计:

  • SDK 提供 CollectClient。
  • autoconfigure 提供 CollectClientAutoConfiguration。
  • @ConfigurationProperties 承接 endpoint、timeout、token。
  • @ConditionalOnMissingBean 允许业务覆盖默认客户端。
  • HealthIndicator 暴露外部依赖状态。
  • MeterBinder 统计调用次数、耗时、失败率。
  • FailureAnalyzer 在缺少 endpoint 时给出友好提示。

验收问题:

  • 为什么 starter 不应该直接写业务表逻辑?
  • 为什么默认 Bean 要加 @ConditionalOnMissingBean?
  • 为什么健康检查要区分存活和就绪?

场景 3:生产接口突然变慢 ​

排查顺序:

  1. 通过网关日志或应用日志拿到 traceId。
  2. 看接口耗时分布,是所有接口慢还是单接口慢。
  3. 看 Actuator metrics:Tomcat 线程、连接池、JVM、HTTP server requests。
  4. 看数据库慢 SQL、锁等待、连接池等待。
  5. 看 Redis timeout、MQ 堆积、外部 HTTP 超时。
  6. 看线程栈是否大量阻塞在同一个资源。
  7. 看 GC 日志和内存是否异常。

不要只说“加缓存”。如果根因是连接池耗尽、下游超时、线程池队列堆积,加缓存可能掩盖问题甚至扩大故障。

阶段 10:面试闭环 ​

面试页负责标准回答,本页负责原理。学习时按下面方式闭环:

mermaid
flowchart TD
    A["先看面试题"] --> B["知道面试问什么"]
    B --> C["回到知识点页理解原理"]
    C --> D["写 Demo 验证"]
    D --> E["补商业场景和排查话术"]
    E --> F["回面试页组织回答"]

高频问题必须能答到这个深度:

面试题合格回答深度
Spring Boot 自动配置原理说清 @EnableAutoConfiguration、候选类、Boot 2/3 声明差异、条件注解、BeanDefinition、IOC 创建
Starter 为什么开箱即用说清 starter、autoconfigure、SDK 边界,以及 @ConditionalOnMissingBean 让用户覆盖
配置为什么能绑定对象说清 PropertySource、Environment、Binder、宽松绑定、类型转换、校验、Actuator 排查
扩展点怎么选说清生命周期阶段,配置解密、BeanDefinition 注册、Bean 增强、启动后预热、健康检查分别用什么
启动慢怎么排查说清配置中心、Bean 创建、外部连接、Runner、端口、线程栈、Actuator startup
接口慢怎么排查说清 traceId、指标、线程池、连接池、DB、Redis、MQ、外部接口、GC

面试标准回答看:Spring Boot 面试题。

最终验收题 ​

你可以用下面的问题检查自己是否真正学会:

  1. @SpringBootApplication 由哪些核心注解组成?每个注解解决什么问题?
  2. 从 SpringApplication.run 到端口监听成功,中间有哪些关键阶段?
  3. 自动配置类是怎么被发现的?Boot 2 和 Boot 3 声明方式有什么区别?
  4. @ConditionalOnClass、@ConditionalOnMissingBean、@ConditionalOnProperty 分别适合什么场景?
  5. 为什么 @ConditionalOnMissingBean 要尊重用户自定义 Bean?
  6. 为什么配置解密不能放在 ApplicationRunner?
  7. @Value 和 @ConfigurationProperties 怎么选?
  8. 一个 Starter 为什么最好拆成 SDK、autoconfigure、starter 三层?
  9. 内嵌 Tomcat 什么时候创建?一次请求如何进入 Controller?
  10. Filter、Interceptor、ArgumentResolver、ResponseBodyAdvice 分别解决什么问题?
  11. Actuator 的 /conditions、/env、/configprops 分别用于排查什么?
  12. Liveness 和 Readiness 为什么不能混用?
  13. 生产接口慢时,为什么不能第一反应只说“加缓存”?
  14. 自定义 HealthIndicator 有什么风险?如何避免探针把依赖打爆?
  15. 如何把医疗采集客户端做成一个可复用 Starter?

本章小结 ​

Spring Boot 的主线不是“少写配置”,而是“用一套可解释、可覆盖、可排查的默认机制把应用工程化”。真正掌握 Spring Boot,要把启动流程、配置体系、自动配置、Starter、内嵌 WebServer、请求链路、扩展点和 Actuator 串起来。这样你不仅会用,还能解释为什么这样设计;线上出问题时,也知道从哪一层开始拆。