@ConditionalOnMissingBean 注解

#Spring #Bean #注解 #条件装配

@ConditionalOnMissingBean 注解详解

核心概念

@ConditionalOnMissingBean 是 Spring Boot 提供的条件注解,用于控制 Bean 的创建条件

总结:如果没有就用我的,有了就用你的。


语法格式

    @Bean
    @ConditionalOnMissingBean(需要检查的类.class)  // 注意:不需要引号
    public BeanType beanName() {
        return new BeanType();
    }

主要属性

属性 说明 示例
value 按类型指定要检查的 Bean 类 @ConditionalOnMissingBean(DataSource.class)
name 按名称指定要检查的 Bean @ConditionalOnMissingBean(name = "myService")
search 限制搜索的 ApplicationContext 层级 @ConditionalOnMissingBean(search = SearchStrategy.CURRENT)

工作原理

┌─────────────────────────────────────────────────────────────┐
│                     @ConditionalOnMissingBean                 │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   检查容器中是否存在目标 Bean                                  │
│                                                              │
│   ┌─────────────┐    存在?    ┌──────────────┐              │
│   │ 目标 Bean   │ ─────────>  │  不创建当前Bean│              │
│   └─────────────┘    是      └──────────────┘              │
│                                                              │
│   ┌─────────────┐    不存在   ┌──────────────┐              │
│   │ 目标 Bean   │ ─────────>  │  创建当前 Bean│              │
│   └─────────────┘            └──────────────┘              │
│                                                              │
└─────────────────────────────────────────────────────────────┘

使用场景

  1. 提供默认实现:当用户没有自定义 Bean 时提供默认值
  2. 避免 Bean 冲突:防止创建重复的 Bean
  3. 可扩展的自动配置:允许用户覆盖默认配置
  4. 按需加载:只在需要时才创建 Bean

代码示例

1. 基本用法(按类型检查)

@Configuration
public class DefaultDataSourceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(DataSource.class)
    public DataSource defaultDataSource() {
        System.out.println("创建默认数据源");
        return new HikariDataSource();
    }
}

2. 按名称检查

@Bean
@ConditionalOnMissingBean(name = "myCustomService")
public MyService myService() {
    return new MyService();
}

3. 默认返回类型检查

@Bean
@ConditionalOnMissingBean  // 等价于 @ConditionalOnMissingBean(CacheService.class)
public CacheService cacheService(CacheManager cacheManager) {
    return new CacheService(cacheManager);
}

4. 完整示例:用户可覆盖的自动配置

@Configuration
@EnableConfigurationProperties(CacheProperties.class)
public class CacheAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "app.cache", name = "enabled", havingValue = "true")
    public CacheManager cacheManager(CacheProperties properties) {
        RedisCacheManager manager = new RedisCacheManager();
        manager.setHost(properties.getHost());
        manager.setPort(properties.getPort());
        manager.setTtl(properties.getTtl());
        return manager;
    }
}

不加注解会出现什么问题?

问题:Bean 冲突导致启动失败

不加注解的情况:

// 框架自动配置
@Configuration
public class DefaultConfig {

    @Bean  // ❌ 没有加 @ConditionalOnMissingBean
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

// 用户自定义配置
@Configuration
public class MyConfig {

    @Bean
    public DataSource dataSource() {
        return new MysqlDataSource();
    }
}

运行结果:报错!

***************************
APPLICATION FAILED TO START
***************************

The bean 'dataSource', defined in DefaultConfig,
could not be registered. A bean with that name has already been defined
and overriding is disabled.

加注解的情况

// 框架自动配置
@Configuration
public class DefaultConfig {

    @Bean
    @ConditionalOnMissingBean(DataSource.class)  // ✅ 加上这个注解
    public DataSource dataSource() {
        System.out.println("创建默认数据源");
        return new HikariDataSource();
    }
}

// 用户自定义配置
@Configuration
public class MyConfig {

    @Bean
    public DataSource dataSource() {
        System.out.println("创建自定义数据源");
        return new MysqlDataSource();
    }
}

运行结果:

  • 用户定义了 DataSource → 使用用户的,默认的不会创建
  • 用户没定义 DataSource → 使用默认的
// 用户定义了 DataSource,控制台输出:
创建自定义数据源

// 用户没定义 DataSource,控制台输出:
创建默认数据源

对比总结

情况 不加注解 加注解
用户定义了 Bean ❌ Bean 冲突,启动失败 ✅ 使用用户的 Bean
用户没定义 Bean ✅ 创建默认 Bean ✅ 创建默认 Bean

类级别 vs 方法级别

// 方法级别:只阻止该Bean的注册
@Configuration
public class MyConfig {
    @Bean
    @ConditionalOnMissingBean
    public MyBean myBean() { ... }  // 只影响这个Bean

    @Bean
    public OtherBean otherBean() { ... }  // 不受影响
}

// 类级别:阻止整个配置类作为Bean注册
@Configuration
@ConditionalOnMissingBean
public class MyConfig {
    @Bean
    public MyBean myBean() { ... }  // 条件不匹配时整个类都不会注册

    @Bean
    public OtherBean otherBean() { ... }
}

@ConditionalOnBean 的对比

@ConditionalOnBean(DataSource.class)        // 容器中存在DataSource时创建
@ConditionalOnMissingBean(DataSource.class)  // 容器中不存在DataSource时创建

这两个注解互为相反的条件,常用于实现"可选依赖"或"默认回退"的配置模式。


核心要点

  1. 使用场景:方便被替换 - 完全正确
  2. 效果:如果用户自己实现了这个类就不会注入了 - 完全正确
  3. 语法@ConditionalOnMissingBean(类.class) - 不需要引号

Reference