Skip to content

Spring Boot 商业场景训练营

Spring Boot 不能只会写 Controller,也不能只背“自动配置、Starter、约定优于配置”。商业项目里真正要会的是:把一个可复用能力做成 Starter,知道配置如何绑定,知道默认 Bean 为什么生效,知道用户 Bean 为什么能覆盖默认实现,知道自动配置没生效怎么查,知道健康检查、指标、失败诊断、启动扩展点分别放在哪里。

训练目标:用“医疗采集客户端 Starter”把 Spring Boot 自动配置、Starter、配置体系、扩展点、Actuator 和排查流程跑通。

训练总流程

mermaid
flowchart TD
    A["识别公共能力"] --> B["拆分 SDK / autoconfigure / starter"]
    B --> C["定义配置属性"]
    C --> D["编写自动配置类"]
    D --> E["配置条件注解"]
    E --> F["注册自动配置清单"]
    F --> G["接入业务项目验证"]
    G --> H["补健康检查和指标"]
    H --> I["用 conditions 排查"]

训练一:为什么要做 Starter

场景

医疗数据采集平台里,多个服务都要调用医院侧采集网关。每个服务都需要网关地址、超时时间、token、重试次数、健康检查和指标。如果每个项目都复制一份配置和客户端代码,后期改协议会非常痛苦。

正确拆分

模块职责是否包含 Spring Boot
collect-client-sdk纯 Java 客户端、请求对象、响应对象不依赖 Boot
collect-client-spring-boot-autoconfigure自动配置、属性类、条件注解、健康检查依赖 Boot
collect-client-spring-boot-starter聚合依赖入口通常只放依赖
mermaid
flowchart TD
    A["业务项目引入 starter"] --> B["starter 带入 autoconfigure"]
    B --> C["autoconfigure 带入 SDK"]
    C --> D["Boot 读取自动配置类"]
    D --> E["条件满足则创建 CollectClient"]

不这样会怎样

错误做法后果
每个项目复制客户端代码协议升级时到处改
Starter 里写业务规则公共组件和业务耦合
不给用户覆盖默认 Bean业务项目无法自定义连接池、鉴权、重试
配置写死多环境部署困难,密钥风险高

训练二:配置属性类

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

// 本训练以 Boot 2.7 / JDK 8 为可运行基线。
// Boot 3 / Spring 6 才改为 jakarta.validation.constraints.NotBlank。
import javax.validation.constraints.NotBlank;
import java.time.Duration;

@Validated
@ConfigurationProperties(prefix = "collect.client")
public class CollectClientProperties {

    private boolean enabled = true;

    @NotBlank
    private String baseUrl;

    private Duration connectTimeout = Duration.ofSeconds(3);

    private Duration readTimeout = Duration.ofSeconds(10);

    private int maxRetry = 2;

    private String token;

    // getter/setter 省略
}

业务项目配置:

yaml
collect:
  client:
    enabled: true
    base-url: https://gateway.hospital.example
    connect-timeout: 3s
    read-timeout: 10s
    max-retry: 2
    token: ${COLLECT_GATEWAY_TOKEN}

原理

Boot 启动时会把配置源加载到 Environment,再由 Binder 根据 collect.client 前缀绑定到 CollectClientPropertiesbase-url 能绑定到 baseUrl,是因为 Spring Boot 支持宽松绑定。

mermaid
flowchart TD
    A["application.yml / 环境变量"] --> B["Environment"]
    B --> C["Binder"]
    C --> D["@ConfigurationProperties"]
    D --> E["CollectClientProperties Bean"]

训练三:自动配置类

java
import org.springframework.boot.autoconfigure.AutoConfiguration;
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;

@AutoConfiguration
@ConditionalOnClass(CollectClient.class)
@EnableConfigurationProperties(CollectClientProperties.class)
@ConditionalOnProperty(prefix = "collect.client", name = "enabled", havingValue = "true", matchIfMissing = true)
public class CollectClientAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public CollectClient collectClient(CollectClientProperties properties) {
        return new CollectClient(
            properties.getBaseUrl(),
            properties.getConnectTimeout(),
            properties.getReadTimeout(),
            properties.getMaxRetry(),
            properties.getToken()
        );
    }
}

条件注解为什么重要

条件作用不加会怎样
@ConditionalOnClassSDK 存在才装配缺类时报错
@ConditionalOnProperty提供开关业务无法关闭
@ConditionalOnMissingBean用户自定义优先默认 Bean 覆盖业务 Bean
@EnableConfigurationProperties注册配置属性配置无法绑定成 Bean

训练四:Boot 2 和 Boot 3 自动配置清单

Boot 2 常见写法

META-INF/spring.factories

properties
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.collect.CollectClientAutoConfiguration

Boot 3 推荐写法

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

text
com.example.collect.CollectClientAutoConfiguration

面试回答

text
Boot 2.6及更早常见通过 spring.factories 的 EnableAutoConfiguration key 声明自动配置类;Boot 2.7 已支持并推荐 AutoConfiguration.imports 且仍兼容旧方式;Boot 3 使用 imports,每行一个自动配置类。这些机制本质上都是给 AutoConfigurationImportSelector 提供候选自动配置类,再由条件注解筛选,最终注册 BeanDefinition。

训练五:用户自定义 Bean 覆盖默认实现

业务项目想自定义客户端:

java
@Configuration
public class CollectClientCustomConfig {

    @Bean
    public CollectClient collectClient() {
        return new CollectClient("https://mock.example", Duration.ofSeconds(1), Duration.ofSeconds(2), 0, "mock");
    }
}

因为自动配置中有 @ConditionalOnMissingBean,容器里已经有用户 Bean 时,默认 CollectClient 不会创建。

mermaid
flowchart TD
    A["解析自动配置"] --> B{"容器里已有 CollectClient"}
    B -- "有" --> C["跳过默认 Bean"]
    B -- "没有" --> D["创建默认 CollectClient"]

训练六:健康检查和指标

HealthIndicator

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

public class CollectClientHealthIndicator implements HealthIndicator {

    private final CollectClient collectClient;

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

    @Override
    public Health health() {
        boolean ok = collectClient.ping();
        if (ok) {
            return Health.up().withDetail("collectGateway", "reachable").build();
        }
        return Health.down().withDetail("collectGateway", "unreachable").build();
    }
}

自动配置:

java
@Bean
@ConditionalOnMissingBean
public CollectClientHealthIndicator collectClientHealthIndicator(CollectClient client) {
    return new CollectClientHealthIndicator(client);
}

指标

可以用 Micrometer 统计请求次数、失败次数、耗时:

java
Timer.Sample sample = Timer.start(meterRegistry);
try {
    collectClient.collect(request);
    meterRegistry.counter("collect.client.request", "result", "success").increment();
} catch (Exception e) {
    meterRegistry.counter("collect.client.request", "result", "fail").increment();
    throw e;
} finally {
    sample.stop(meterRegistry.timer("collect.client.latency"));
}

训练七:FailureAnalyzer 让错误更好懂

配置缺失时,普通异常可能很长。可以用 FailureAnalyzer 输出更清晰的失败原因和处理建议。

java
public class CollectClientFailureAnalyzer
    extends AbstractFailureAnalyzer<CollectClientConfigException> {

    @Override
    protected FailureAnalysis analyze(Throwable rootFailure,
                                      CollectClientConfigException cause) {
        return new FailureAnalysis(
            "采集客户端配置错误:" + cause.getMessage(),
            "请检查 collect.client.base-url、token、enabled 配置,或关闭 collect.client.enabled。",
            cause
        );
    }
}

注册:

properties
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.collect.CollectClientFailureAnalyzer

训练八:自动配置没生效怎么查

打开条件报告

yaml
debug: true

或使用 Actuator:

bash
curl http://localhost:8080/actuator/conditions
curl http://localhost:8080/actuator/configprops
curl http://localhost:8080/actuator/env

排查流程

mermaid
flowchart TD
    A["Starter 没生效"] --> B["确认依赖是否引入"]
    B --> C["确认自动配置清单是否存在"]
    C --> D["看 conditions 报告"]
    D --> E{"条件是否匹配"}
    E -- "否" --> F["查 classpath / 配置项 / exclude"]
    E -- "是" --> G{"Bean 是否创建失败"}
    G -- "是" --> H["看异常和 FailureAnalyzer"]
    G -- "否" --> I["查是否已有用户 Bean 覆盖"]

常见原因

现象原因
自动配置类不在报告里清单文件路径错、依赖没引入
@ConditionalOnClass 不匹配SDK 类不在 classpath
@ConditionalOnProperty 不匹配配置项写错或开关关闭
默认 Bean 没创建用户已经自定义 Bean
配置属性为空prefix 写错、配置源被覆盖、Profile 不对

训练九:扩展点怎么选

需求推荐扩展点
配置解密要在绑定前生效EnvironmentPostProcessor
动态注册一批 BeanDefinitionBeanDefinitionRegistryPostProcessor
修改已有 BeanDefinitionBeanFactoryPostProcessor
给 Bean 初始化前后增强BeanPostProcessor
启动完成后轻量预热ApplicationRunner
请求进入 MVC 前做处理Filter / HandlerInterceptor
Controller 自动注入当前用户HandlerMethodArgumentResolver
暴露健康状态HealthIndicator
暴露业务指标MeterBinder / Micrometer
启动失败给友好提示FailureAnalyzer

关键原则:扩展点要放在正确生命周期阶段。配置解密不能放 Runner,因为 Runner 执行时配置绑定和自动配置早就结束了。

最终验收清单

做完这页后,你应该能回答:

  1. Starter、autoconfigure、SDK 为什么要拆开?
  2. @ConfigurationProperties 为什么比一堆 @Value 更适合公共配置?
  3. 自动配置类是怎么被发现的?
  4. Boot 2 和 Boot 3 自动配置清单有什么区别?
  5. @ConditionalOnMissingBean 为什么重要?
  6. Starter 没生效怎么排查?
  7. /actuator/conditions/actuator/configprops/actuator/env 分别看什么?
  8. HealthIndicator 和业务指标怎么加?
  9. FailureAnalyzer 解决什么问题?
  10. 配置解密为什么应该放 EnvironmentPostProcessor
  11. Runner 为什么不适合做不可控长任务?

关联知识点

知识点入口
Spring Boot 主线从零到生产级掌握
自动配置全过程自动配置与扩展点全过程
Starter 全过程Starter 全过程
配置体系配置体系全过程
启动流程启动流程全过程
扩展点Spring Boot 扩展点
面试Spring Boot 面试题