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 { Class<?>[] value() default {};
String[] type() default {};
Class<? extends Annotation>[] annotation() default {};
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(); }
@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 {};
Class<?>[] ignored() default {};
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
|
@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 {};
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
|
@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
|
@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
Be the first person to leave a comment!