在 Spring Boot 的魔法书里,@ConditionalOnMissingBean 可能是最能体现“大度”的注解了。它的核心逻辑非常简单:“如果你没准备,那我就亲自动手;如果你已经准备了,我就靠边站。”

它是实现 Spring Boot 自动配置(Auto-configuration) 的基石。


1. 为什么要用它?

当你编写一个 Library(库)或 Starter 给别人用时,你通常会提供一个默认的 Bean 实现。但你又不想“独断专行”,万一用户想用自己定义的 Bean 替代你的默认实现呢?

如果没有这个注解,Spring 容器会因为发现两个同类型的 Bean 而抛出 NoUniqueBeanDefinitionException。有了它,用户定义的 Bean 永远拥有最高优先权


2. 基本语法

你可以把它标注在 @Bean 方法上,或者直接标注在类上。

@Configuration
public class MyAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(Service.class)
    public Service defaultService() {
        return new DefaultServiceImpl();
    }
}

常用属性:

  • value / type: 检查容器中是否存在指定类型的 Bean(最常用)。

  • name: 检查容器中是否存在指定名称的 Bean。

  • annotation: 检查是否有 Bean 被标注了某个特定的注解。


3. 实战案例:自定义短信发送器

假设你写了一个短信插件,默认使用阿里云,但允许用户自定义。

第一步:定义接口

Java

public interface SmsSender {
    void send(String message);
}

第二步:自动配置类

@Configuration
public class SmsAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(SmsSender.class) // 如果用户没定义 SmsSender,执行这个方法
    public SmsSender aliyunSmsSender() {
        System.out.println("检测到未定义 SmsSender,加载默认阿里云实现...");
        return new AliyunSmsSender();
    }
}

第三步:用户自定义(可选)

如果用户在自己的代码里写了下面这段,上面的 aliyunSmsSender 就会自动失效

@Component
public class MyTencentSmsSender implements SmsSender {
    @Override
    public void send(String message) {
        System.out.println("使用腾讯云发送短信...");
    }
}

4. 避坑指南(非常重要!)

在使用这个注解时,最容易犯的错误就是加载顺序问题

金律: Spring 必须先扫描到“用户自定义的 Bean”,然后才能判断出“是否缺失”。

  1. 放在自动配置类中: 这种注解通常用在 src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 声明的配置类里。Spring Boot 会保证先加载用户的 @Component,再加载这些自动配置。

  2. 避免在同一个配置类内竞争: 如果你在同一个 @Configuration 类里写了两个同类型的 Bean,且都加了该注解,结果可能取决于方法的声明顺序,这通常不是你想要的。


5. 总结

特性

说明

核心功能

只有当容器中没有指定 Bean 时,才创建当前 Bean。

主要用途

编写 Starter、提供默认实现、解耦。

对手

@ConditionalOnBean(只有存在时才创建)。