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 前缀绑定到 CollectClientProperties。base-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 面试题