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:

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

再次运行:

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

相关推荐
小马爱打代码2 分钟前
Spring AI:ChatMemory 实现聊天记忆功能
java·人工智能·spring
小许学java6 分钟前
数据结构-模拟实现顺序表和链表
java·数据结构·链表·arraylist·linkedlist·顺序表模拟实现·链表的模拟实现
康不坦丁8 分钟前
MySQL 的 order by 简化(使用列序号和列别名排序)
后端·mysql
wadesir21 分钟前
深入理解Rust静态生命周期(从零开始掌握‘static的奥秘)
开发语言·后端·rust
+VX:Fegn089526 分钟前
计算机毕业设计|基于springboot + vue零食商城管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
哈哈哈笑什么34 分钟前
蜜雪冰城1分钱奶茶秒杀活动下,使用分片锁替代分布式锁去做秒杀系统
redis·分布式·后端
Query*42 分钟前
杭州2024.08 Java开发岗面试题分类整理【附面试技巧】
java·开发语言·面试
WZTTMoon1 小时前
Spring Boot 4.0 迁移核心注意点总结
java·spring boot·后端
寻kiki1 小时前
scala 函数类?
后端
疯狂的程序猴1 小时前
iOS App 混淆的真实世界指南,从构建到成品 IPA 的安全链路重塑
后端