设计模式与Spring Boot:自动配置中的工厂、Starter中的外观、Actuator中的观察者

说实话,Spring Boot 能火起来,靠的不是什么黑魔法。它背后藏着一套设计模式的组合拳。我这些年看过的项目里,很多人会用 Spring Boot,但真要问起「自动配置为什么能自动?」「Starter 为什么加个依赖就能用?」「Actuator 怎么做到实时监控的?」——能讲清楚的不多。

今天咱们就掰开揉碎,聊聊这三个核心机制背后的设计模式。工厂、外观、观察者,这三个模式在 Spring Boot 里被用到了极致。

自动配置中的工厂模式

先问个问题:Spring Boot 启动时,那些 @ConditionalOnClass@ConditionalOnMissingBean 是怎么工作的?

说白了,这就是一个工厂模式的变体。Spring Boot 内部维护了一个「自动配置候选列表」,启动时遍历这些候选类,判断条件是否满足,满足就创建 Bean。

核心思想:将对象的创建逻辑封装在配置类中,由条件注解决定是否「生产」这个对象。

我记得有一次排查一个项目启动慢的问题。翻日志发现,某个自动配置类加载了,但条件不满足,白白浪费了类加载时间。后来我建议团队在 spring.factories 里精简自动配置项,启动速度快了 30%。

来看一个简化版的自动配置工厂:

// 自动配置类,相当于工厂方法
@Configuration
@ConditionalOnClass(name = "com.example.SomeService")
public class SomeServiceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public SomeService someService() {
        return new SomeServiceImpl();
    }
}

// spring.factories 中注册
// org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
// com.example.autoconfigure.SomeServiceAutoConfiguration

这里 SomeServiceAutoConfiguration 就是一个工厂。它根据条件决定是否生产 SomeService 实例。你想想看,如果没有这个模式,每个项目都得手动写一堆 @Bean,那 Spring Boot 还谈什么「开箱即用」?

避坑指南:我曾经在项目中遇到自动配置冲突的问题。两个 Starter 都试图创建同一个类型的 Bean,结果启动报错。解决方案是用 @ConditionalOnMissingBean 加上 @Order 控制优先级。

Starter 中的外观模式

Starter 是什么?说白了就是一个「打包好的依赖集合」。你加一个 spring-boot-starter-data-redis,Redis 的连接池、序列化器、模板类全都有了。

这背后就是外观模式。Starter 把一堆复杂的配置、依赖、Bean 定义全部封装起来,对外只暴露一个简单的坐标(GAV)。

我个人习惯在设计 Starter 时,遵循三个原则:

  • 最小依赖原则:只引入必要的依赖,避免传递依赖污染
  • 自动配置 + 手动覆盖:提供默认配置,但允许用户通过 application.yml 覆盖
  • 条件注解兜底:用 @ConditionalOnProperty 控制开关

来看一个自定义 Starter 的结构:

my-starter/
├── pom.xml
├── src/main/java/com/example/starter/
│   ├── MyService.java              // 核心服务接口
│   ├── MyServiceImpl.java          // 默认实现
│   ├── MyProperties.java           // 配置属性类
│   └── MyServiceAutoConfiguration.java  // 自动配置
└── src/main/resources/
    └── META-INF/
        └── spring.factories        // 注册自动配置

用户使用时只需要:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>my-starter</artifactId>
    <version>1.0.0</version>
</dependency>

就这一行,所有配置都到位了。这就是外观模式的魅力——把复杂性藏起来,给用户一个简单的入口。

注意:外观模式不是「万能胶」。我见过有人把整个业务逻辑都塞进 Starter 里,结果升级时牵一发动全身。Starter 只应该做「集成」和「配置」,不要包含业务代码。

Actuator 中的观察者模式

Actuator 是 Spring Boot 的监控模块。它怎么知道应用的健康状态?怎么收集指标数据?

答案是观察者模式。Actuator 内部维护了一组 HealthIndicator,每个 Indicator 都是一个观察者。当调用 /actuator/health 时,Actuator 会遍历所有 Indicator,收集它们的健康状态。

我画了一张图,帮你理解这个流程:

HealthEndpoint (主题/被观察者) DiskHealthIndicator DbHealthIndicator RedisHealthIndicator 聚合健康状态 {"status":"UP","components":{...}} GET /actuator/health

你看,HealthEndpoint 是主题,它不关心具体有哪些 Indicator。你加一个新的 HealthIndicator,它自动就能感知到。这就是观察者模式的核心——松耦合。

代码层面更简单:

// 自定义健康指标,自动被 Actuator 发现
@Component
public class CustomHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        boolean isHealthy = checkExternalService();
        if (isHealthy) {
            return Health.up().withDetail("service", "running").build();
        }
        return Health.down().withDetail("service", "unreachable").build();
    }

    private boolean checkExternalService() {
        // 模拟检查
        return true;
    }
}

你只需要实现 HealthIndicator 接口,Spring Boot 会自动把它注册到观察者列表中。调用 /actuator/health 时,所有 Indicator 的结果会被聚合起来。

经验之谈:我曾经在微服务项目中,给每个服务都加了自定义的 HealthIndicator,用来检查下游依赖是否可用。这样网关层做负载均衡时,可以直接根据健康状态剔除故障节点。效果非常好。

三种模式的协同

这三种模式在 Spring Boot 里不是孤立的。它们互相配合,构成了一个完整的生态:

模式 Spring Boot 中的角色 核心价值
工厂模式 自动配置(@Conditional 系列) 按需创建 Bean,减少手动配置
外观模式 Starter(spring-boot-starter-*) 封装复杂依赖,提供统一入口
观察者模式 Actuator(HealthIndicator、Metrics) 松耦合收集状态,支持扩展

你想想看,如果没有工厂模式,自动配置就做不到「按需加载」;没有外观模式,Starter 就做不到「一键集成」;没有观察者模式,Actuator 就做不到「动态扩展」。

这三个模式,说白了就是 Spring Boot 的「三驾马车」。理解它们,你才算真正看懂了 Spring Boot 的设计哲学。

总结一下:工厂模式管「创建」,外观模式管「封装」,观察者模式管「通知」。下次面试官问你 Spring Boot 的设计模式,你就从这三个角度切入,保证能说到点子上。


公众号:蓝海资料掘金营,微信deep3321