【基础系列】Bean之@ConditionalOnBean与@ConditionalOnClass

文章目录
  1. I. Bean的存在与否作为条件
    1. 1. @ConditionalOnBean
      1. a. 注解定义
      2. b. 测试用例
    2. 2. ConditionalOnMissingBean
      1. a. 接口定义
      2. b. 测试用例
    3. 3. 实例演示
  2. II. Class的存在与否作为条件
    1. 1. @ConditionalOnClass
      1. a. 注解定义
      2. b. 测试用例
    2. 2. @ConditionalOnMissingClass
      1. a. 注解定义
      2. b. 测试用例
    3. 3. 实例演示
  3. III. 其他
    1. 0. 相关
      1. a. 更多博文
      2. b. 项目源码
    2. 1. 一灰灰Blog
    3. 2. 声明
    4. 3. 扫描关注

bean的条件注入,除了前面一篇博文中介绍的通过@Conditional注解配合Condition接口的实现之外,还提供了更多简化的注解使用方式,省略了自己实现Condtion接口,本篇博文主要介绍下面几个常用的注解使用方式

  • @ConditionalOnBean
  • @ConditionalOnMissingBean
  • @ConditionalOnClass
  • @ConditionalOnMissingClass

I. Bean的存在与否作为条件

当Bean不存在时,创建一个默认的Bean,在Spring的生态中可以说比较常见了;接下来看下这种方式可以怎么用

1. @ConditionalOnBean

要求bean存在时,才会创建这个bean;如我提供了一个bean名为RedisOperBean,用于封装redis相关的操作;但是我这个bean需要依赖restTemplate这个bean,只有当应用引入了redis的相关依赖,并存在RestTemplate这个bean的时候,我这个bean才会生效

假设bean的定义如下

1
2
3
4
5
6
7
8
@Component
@ConditionalOnBean(name="redisTemplate")
public class RedisOperBean {
private final RedisTemplate redisTemplate;
public RedisOperBean(RedisTemplate redisTemplate) {
// ...
}
}

这样的好处就是我提供的这个第三方包,如果被用户A间接依赖(但是A本身不需要操作redis),也不会因为创建RedisOperBean而抛异常

产生异常的原因是因为找不到RestTemplate的bean,因此无法实例化RedisOperBean,从而抛出异常

a. 注解定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
// bean类型
Class<?>[] value() default {};

// bean类型
String[] type() default {};

// 要求bean上拥有指定的注解
Class<? extends Annotation>[] annotation() default {};

// bean names
String[] name() default {};

SearchStrategy search() default SearchStrategy.ALL;
}

b. 测试用例

构建一个简单的测试用例,先定义一个基础的bean

1
2
public class DependedBean {
}

再定义一个依赖只有上面的bean存在时,才会加载的bean

1
2
3
4
5
6
7
8
9
10
11
12
public class LoadIfBeanExist {

private String name;

public LoadIfBeanExist(String name) {
this.name = name;
}

public String getName() {
return "load if bean exists: " + name;
}
}

接下来就是bean的定义了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Bean
public DependedBean dependedBean() {
return new DependedBean();
}

/**
* 只有当DependedBean 存在时,才会创建bean: `LoadIfBeanExist`
*
* @return
*/
@Bean
@ConditionalOnBean(name = "dependedBean")
public LoadIfBeanExist loadIfBeanExist() {
return new LoadIfBeanExist("dependedBean");
}

根据上面的测试用例,LoadIfBeanExist是会被正常加载的; 具体结果看后面的实例演示

2. ConditionalOnMissingBean

和前面一个作用正好相反的,上面是要求存在bean,而这个是要求不存在

a. 接口定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public @interface ConditionalOnMissingBean {
Class<?>[] value() default {};

String[] type() default {};

/**
* The class type of beans that should be ignored when identifying matching beans.
*/
Class<?>[] ignored() default {};

/**
* The class type names of beans that should be ignored when identifying matching
* beans.
*/
String[] ignoredType() default {};

Class<? extends Annotation>[] annotation() default {};

String[] name() default {};

SearchStrategy search() default SearchStrategy.ALL;
}

b. 测试用例

同样定义一个bean不存在时,才创建的bean

1
2
3
4
5
6
7
8
9
10
11
public class LoadIfBeanNotExists {
public String name;

public LoadIfBeanNotExists(String name) {
this.name = name;
}

public String getName() {
return "load if bean not exists: " + name;
}
}

对应的bean配置如下

1
2
3
4
5
6
7
8
9
10
/**
* 只有当没有notExistsBean时,才会创建bean: `LoadIfBeanNotExists`
*
* @return
*/
@Bean
@ConditionalOnMissingBean(name = "notExistsBean")
public LoadIfBeanNotExists loadIfBeanNotExists() {
return new LoadIfBeanNotExists("notExistsBean");
}

因为没有notExistsBean,所以上面这个bean也应该被正常注册

3. 实例演示

因为bean的是否存在和class的是否存在有较大的相似性,因此实例演示放在下一小节,一起测试

II. Class的存在与否作为条件

从使用来看,和前面基本上没有太大的区别,无非就是将bean换成了class;这样就可以避免因为Class Not Found导致的编译异常了

1. @ConditionalOnClass

要求class存在

a. 注解定义

1
2
3
4
5
6
7
8
9
10
public @interface ConditionalOnClass {
Class<?>[] value() default {};

/**
* The classes names that must be present.
* @return the class names that must be present.
*/
String[] name() default {};

}

b. 测试用例

先定义一个class

1
2
public class DependedClz {
}

然后依赖class存在的bean

1
2
3
4
5
6
7
8
9
10
11
public class LoadIfClzExists {
private String name;

public LoadIfClzExists(String name) {
this.name = name;
}

public String getName() {
return "load if exists clz: " + name;
}
}

接下来就是Bean的配置

1
2
3
4
5
6
7
8
9
10
/**
* 当引用了 {@link DependedClz} 类之后,才会创建bean: `LoadIfClzExists`
*
* @return
*/
@Bean
@ConditionalOnClass(DependedClz.class)
public LoadIfClzExists loadIfClzExists() {
return new LoadIfClzExists("dependedClz");
}

因为类存在,所以测试时,这个bean应该被正常注册

2. @ConditionalOnMissingClass

class不存在时,才会加载bean

a. 注解定义

1
2
3
public @interface ConditionalOnMissingClass {
String[] value() default {};
}

b. 测试用例

定义一个class缺少时才会创建的bean

1
2
3
4
5
6
7
8
9
10
11
public class LoadIfClzNotExists {
private String name;

public LoadIfClzNotExists(String name) {
this.name = name;
}

public String getName() {
return "load if not exists clz: " + name;
}
}

bean的配置如下

1
2
3
4
5
6
7
8
9
10
/**
* 当系统中没有 com.git.hui.boot.conditionbean.example.depends.clz.DependedClz类时,才会创建这个bean
*
* @return
*/
@Bean
@ConditionalOnMissingClass("com.git.hui.boot.conditionbean.example.depends.clz.DependedClz")
public LoadIfClzNotExists loadIfClzNotExists() {
return new LoadIfClzNotExists("com.git.hui.boot.conditionbean.example.depends.clz.DependedClz");
}

因为上面这个类存在,所以这个bean不应该被正常注册

3. 实例演示

起一个rest服务,测试下上面的四个bean是否正常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@RestController
@RequestMapping("depends")
public class DependRest {

@Autowired
private LoadIfBeanExist loadIfBeanExist;
@Autowired
private LoadIfBeanNotExists loadIfBeanNotExists;
@Autowired
private LoadIfClzExists loadIfClzExists;
@Autowired(required = false)
private LoadIfClzNotExists loadIfClzNotExists;

@GetMapping(path = "show")
public String show() {
Map<String, String> result = new HashMap<>(4);
// 存在
result.put("loadIfBeanExist", loadIfBeanExist == null ? "null ==> false!" : loadIfBeanExist.getName());
// 存在
result.put("loadIfBeanNotExists",
loadIfBeanNotExists == null ? "null ==> false!" : loadIfBeanNotExists.getName());
// 存在
result.put("loadIfClzExists", loadIfClzExists == null ? "null ==> false!" : loadIfClzExists.getName());
// 不存在
result.put("loadIfClzNotExists", loadIfClzNotExists == null ? "null ==> true!" : loadIfClzNotExists.getName());

return JSONObject.toJSONString(result);
}
}

根据前面的分析,返回的结果应该是三个存在,一个不存在;下图执行和我们预期一致

条件依赖注册演示

III. 其他

0. 相关

a. 更多博文

基础篇

应用篇

b. 项目源码

1. 一灰灰Blog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

一灰灰blog

QrCode

知识星球

goals


打赏 如果觉得我的文章对您有帮助,请随意打赏。
分享到