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() 过程中创建和启动。
  • ApplicationRunnerCommandLineRunner 在容器基本可用后执行。

常见误区

误区正确理解
启动类一运行就创建所有 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-millisconnectTimeoutMillisCONNECT_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 安全边界

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

  • 只对公网暴露必要的 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 串起来。这样你不仅会用,还能解释为什么这样设计;线上出问题时,也知道从哪一层开始拆。