Spring Boot 注解 @ConditionalOnMissingBean是什么

一句话总结:

@ConditionalOnMissingBean 是 Spring Boot 提供的一个 条件注解(Conditional Annotation),意思是:

只有当 Spring 容器中 不存在 某个 Bean 时,当前的 Bean 或配置才会被加载。

这是一种典型的"按需装配"策略,常用于自动配置类(Auto Configuration)中。

如果大家有看源码的习惯,会发现到处都有这个注解:

RestTemplate:

Jackson - ObjectMapper:

  • Spring Boot 默认提供的 JSON 解析工具 ObjectMapper

  • 如果你没自己定义一个,它就帮你创建一个默认的

  • 你可以完全覆盖它,比如配置命名策略、日期格式等

框架或者组件库想要提供一个默认实现,但又允许使用者自定义自己的实现。这个时候就可以使用 @ConditionalOnMissingBean。这就达到了 可扩展+默认兜底 的目的。

同理,还有个相对应的是:@ConditionalOnBean

对比:@ConditionalOnBean vs @ConditionalOnMissingBean

注解 含义
@ConditionalOnBean(SomeClass.class) 只有当容器中存在 SomeClass 类型的 Bean 时才执行
@ConditionalOnMissingBean(SomeClass.class) 只有当容器中不存在 SomeClass 类型的 Bean 时才执行

这两个注解是互补的,分别用于不同的场景。

支持的用法形式

java 复制代码
@ConditionalOnMissingBean(SomeClass.class)                 // 类型判断
@ConditionalOnMissingBean(name = "someBeanName")           // 名称判断
@ConditionalOnMissingBean(value = SomeClass.class)         // 等价于上面的类型判断
@ConditionalOnMissingBean(type = "com.example.SomeClass")  // 用字符串形式避免类加载

你也可以组合使用,如:

java 复制代码
@ConditionalOnMissingBean(
    value = SomeInterface.class,
    name = "customBean"
)

特别注意:

不能直接加在 @Component、@Service、@Controller等用于直接注册 Bean 的注解上,因为 Spring 在解析这些注解时并不会走自动配置的判断逻辑,因此不会触发 @ConditionalOnMissingBean 的条件判断,最终会导致Bean 被强制注册或 注册失败报错!

代码示例

自定义一个接口:

java 复制代码
public interface MyService {
    void hello();
}

错误示例

先来一个错误示例(直接在@Component下加上了):

java 复制代码
@Slf4j
@Component
@ConditionalOnMissingBean(MyService.class)
public class MyServiceDefault implements MyService {

    @Override
    public void hello() {
        log.info("use default bean...");
    }
}

项目不会报错,直接启动后查找Bean:

会发现根本没注册上。所以,虽然加在@Component等注解的类上语法允许,但 Spring 不会进行条件判断和直接注册,可能导致意料之外的报错。所以不要这么用。

正确用法:

@Configuration + @Bean 的方式(大家回头看看我最上面截的源码截图中,也都是这种方式)

java 复制代码
@Configuration
public class MyServiceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(MyService.class)
    public MyService myService() {
        return new MyServiceDefault();
    }
}

默认实现类:

java 复制代码
@Slf4j
public class MyServiceDefault implements MyService {

    @Override
    public void hello() {
        log.info("use default bean...");
    }
}

找个地方调用一下:

java 复制代码
public class StarApplication {

    @Autowired
    private MyService myService;

    public static void main(String[] args) {
        SpringApplication.run(StarApplication.class, args);
    }

    @PostConstruct
    public void test() {
        myService.hello();
    }
}

现在模拟用户实现了这个接口:

java 复制代码
@Slf4j
@Component
public class MyServiceUser implements MyService {

    @Override
    public void hello() {
        log.info("use self bean...");
    }
}

可以看到就一个Bean:

当然,你也可以去掉这个注解试试:

再次运行:

可以看到两个地方都注册上了。

相关推荐
wowocpp1 小时前
springboot IOC
spring boot
白日依山尽yy1 小时前
SpringBoot Day_03|数据校验|异常处理|日志级别|定时器
java·spring boot·spring
Java&Develop1 小时前
springboot 集成kerberos 用户认证 获取域账号
java·spring boot·后端
会飞的架狗师1 小时前
【SpringBoot实战指南】使用 Spring Cache
spring boot·后端·spring·缓存
努力也学不会java3 小时前
【RabbitMQ】 RabbitMQ高级特性(二)
java·分布式·后端·中间件·rabbitmq
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ4 小时前
crud方法命名示例
java·开发语言
多则惑少则明4 小时前
java 代码查重(三)常见的距离算法和相似度(相关系数)计算方法
java·算法·常见的距离算法和相似度
天若有情6736 小时前
探秘 C++ 计数器类:从基础实现到高级应用
java·开发语言·c++
进击的愤怒6 小时前
GIM发布新版本了 (附rust CLI制作brew bottle流程)
开发语言·后端·rust