@ConditionalOnBean与@ConditionalOnClass

@ConditionalOnMissingBean,它是修饰bean的一个注解,主要实现的是,当你的bean被注册之后,如果而注册相同类型的bean,就不会成功,它会保证你的bean只有一个,即你的实例只有一个,当你注册多个相同的bean时,会出现异常,以此来告诉开发人员。

代码演示

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

@Component

public class AutoConfig {

  @Bean

  public AConfig aConfig() {

    return new AConfig("lind");

  }

 

  @Bean

  @ConditionalOnMissingBean(AMapper.class)

  public AMapper aMapper1(AConfig aConfig) {

    return new AMapperImpl1(aConfig);

  }

 

  @Bean

  public AMapper aMapper2(AConfig aConfig) {

    return new AMapperImpl2(aConfig);

  }

}

因为在aMapper1上面标识了AMapper类型的bean只能有一个实现 @ConditionalOnMissingBean(AMapper.class),所以在进行aMapper2注册时,系统会出现上面图上的异常,这是正常的。

当我们把 @ConditionalOnMissingBean(AMapper.class) 去掉之后,你的bean可以注册多次,这时需要用的@Primary来确定你要哪个实现;一般来说,对于自定义的配置类,我们应该加上@ConditionalOnMissingBean注解,以避免多个配置同时注入的风险。

@Primary标识哪个是默认的bean

1

2

3

4

5

6

7

8

9

10

@Bean

public AMapper aMapper1(AConfig aConfig) {

  return new AMapperImpl1(aConfig);

}

 

@Bean

@Primary

public AMapper aMapper2(AConfig aConfig) {

  return new AMapperImpl2(aConfig);

}

@ConditionalOnProperty

通过其三个属性prefix,name以及havingValue来实现的,其中prefix表示配置文件里节点前缀,name用来从application.properties中读取某个属性值,havingValue表示目标值。

  • 如果该值为空,则返回false;
  • 如果值不为空,则将该值与havingValue指定的值进行比较,如果一样则返回true;否则返回false。
  • 返回值为false,则该configuration不生效;为true则生效。

下面代码演示为配置文件lind.redis.enable为true时才会注册RedisFactory这个bean

1

2

3

4

5

6

7

8

@Configuration

@ConditionalOnProperty(prefix="lind.redis",name = "enable", havingValue = "true")

public class RedisConfig {

  @Bean

  public RedisMap redisMap(){

    return new RedisMapImpl();

  }

}

其它注释及总结

  • @ConditionalOnBean // 当给定的在bean存在时,则实例化当前Bean
  • @ConditionalOnMissingBean // 当给定的在bean不存在时,则实例化当前Bean
  • @ConditionalOnClass // 当给定的类名在类路径上存在,则实例化当前Bean
  • @ConditionalOnMissingClass // 当给定的类名在类路径上不存在,则实例化当前Bean

@Conditional可以通过条件控制是否注入Bean,这篇讲下有关Bean其它几个常用的注解使用方式

@ConditionalOnBean         //	当给定的在bean存在时,则实例化当前Bean
@ConditionalOnMissingBean  //	当给定的在bean不存在时,则实例化当前Bean
@ConditionalOnClass        //	当给定的类名在类路径上存在,则实例化当前Bean
@ConditionalOnMissingClass //	当给定的类名在类路径上不存在,则实例化当前Bean

下面我通过案例深入讲下@ConditionalOnBean 注解,这个理解其它也就理解了。

一、@ConditionalOnBean概念

需求场景 比如下面一种场景,我在实例化People对象的时候,需要注入一个City对象。这个时候问题来了,如果city没有实例化,那么下面就会报空指针或者直接报错。
所以这里需求很简单,就是当前city存在则实例化people,如果不存在则不实例化people,这个时候@ConditionalOnBean 的作用来了。

    @Bean
    public People people(City city) {
        //这里如果city实体没有成功注入 这里就会报空指针
        city.setCityName("千岛湖");
        city.setCityCode(301701);
        return new People("小小", 3, city);
    }

1、@ConditionalOnBean注解定义

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
    /**
     * 需要作为条件的类的Class对象数组
     */
    Class<?>[] value() default {};
    /**
     * 需要作为条件的类的Name,Class.getName()
     */
    String[] type() default {};
    /**
     *  (用指定注解修饰的bean)条件所需的注解类
     */
    Class<? extends Annotation>[] annotation() default {};
    /**
     * spring容器中bean的名字
     */
    String[] name() default {};
    /**
     * 搜索容器层级,当前容器,父容器
     */
    SearchStrategy search() default SearchStrategy.ALL;
    /**
     * 可能在其泛型参数中包含指定bean类型的其他类
     */
    Class<?>[] parameterizedContainer() default {};
}

下面举例说明。
 

二、@ConditionalOnBean示例

1、Bean实体

1)City类

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class City {
    /**
     * 城市名称
     */
    private String cityName;
    /**
     * 城市code
     */
    private Integer cityCode;
}

2)People类

这里City作为People一个属性字段。

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class People {
  /**
     * 姓名
     */
    private String name;
    /**
     * 年龄
     */
    private Integer age;
    /**
     *  城市信息
     */
    private City city;
}

2、Config类

这里写个正常的配置类,City成功注入到IOC容器中。

@Slf4j
@Configuration
public class Config {
    @Bean
    public City city() {
        City city = new City();
        city.setCityName("千岛湖");
        return city;
    }
    @Bean
    public People people(City city) {
        //这里如果city实体没有成功注入 这里就会报空指针
        city.setCityCode(301701);
        return new People("小小", 3, city);
    }
}

3、Test测试类

@SpringBootTest(classes = Application.class)
@RunWith(SpringRunner.class)
public class TestConditionOn {

    @Autowired(required=false)
    private People people;
    
    @Test
    public void test() {
        System.out.println("= = = = = = = = = = = = = ");
        System.out.println("people = " + people);
        System.out.println("= = = = = = = = = = = = = ");
    }
}

运行结果

一切正常,这个很符合我们实际开发中的需求。但是如果有一种情况,就是我的city并没有被注入。我把上面这部分注视掉。

//    @Bean
//    public City city() {
//        City city = new City();
//        city.setCityName("千岛湖");
//        return city;
//    }

再运行测试类

发现启动直接报错了,这当然不是我们希望看到的,我们是要当city已经注入那么实例化people,如果没有注入那么不实例化people。

@Slf4j
@Configuration
public class Config {
//    @Bean
//    public City city() {
//        City city = new City();
//        city.setCityName("千岛湖");
//        return city;
//    }

    /**
     * 这里加了ConditionalOnBean注解,就代表如果city存在才实例化people
     */
    @Bean
    @ConditionalOnBean(name = "city")
    public People people(City city) {
        //这里如果city实体没有成功注入 这里就会报空指针
        city.setCityCode(301701);
        return new People("小小", 3, city);
    }
}

再运行测试类

很明显,上面因为city已经注释调,所以也导致无法实例化people,所以people为null。

注意有点要注意的,就是一旦使用@Autowired那就默认代表当前Bean一定是已经存在的,如果为null,会报错。所以这里要修改下。

@Autowired(required=false) //required=false 的意思就是允许当前的Bean对象为null。

总结讲了这个注解,其它三个注解的意思大致差不多,在实际开发过程中可以根据实际情况使用该注解。
GitHub源码 https://github.com/yudiandemingzi/spring-boot-study
项目名称 04-conditionalon

 

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值