SpringBoot基础篇Bean之条件注入之注解使用
bean的条件注入,除了前面一篇博文中介绍的通过@Conditional
注解配合Condition
接口的实现之外,还提供了更多简化的注解使用方式,省略了自己实现Condtion
接口,本篇博文主要介绍下面几个常用的注解使用方式
@ConditionalOnBean
@ConditionalOnMissingBean
@ConditionalOnClass
@ConditionalOnMissingClass
@ConditionalOnProperty
@ConditionalOnExpression
I. Bean的存在与否作为条件
当Bean不存在时,创建一个默认的Bean,在Spring的生态中可以说比较常见了;接下来看下这种方式可以怎么用
1. @ConditionalOnBean
要求bean存在时,才会创建这个bean;如我提供了一个bean名为RedisOperBean
,用于封装redis相关的操作;但是我这个bean需要依赖restTemplate
这个bean,只有当应用引入了redis的相关依赖,并存在RestTemplate
这个bean的时候,我这个bean才会生效
假设bean的定义如下
@Component
@ConditionalOnBean(name="redisTemplate")
public class RedisOperBean {private final RedisTemplate redisTemplate;public RedisOperBean(RedisTemplate redisTemplate) {// ...}
}
复制代码
这样的好处就是我提供的这个第三方包,如果被用户A间接依赖(但是A本身不需要操作redis),也不会因为创建RedisOperBean
而抛异常
产生异常的原因是因为找不到RestTemplate的bean,因此无法实例化RedisOperBean,从而抛出异常
a. 注解定义
@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 namesString[] name() default {};SearchStrategy search() default SearchStrategy.ALL;
}
复制代码
b. 测试用例
构建一个简单的测试用例,先定义一个基础的bean
public class DependedBean {
}
复制代码
再定义一个依赖只有上面的bean存在时,才会加载的bean
public class LoadIfBeanExist {private String name;public LoadIfBeanExist(String name) {this.name = name;}public String getName() {return "load if bean exists: " + name;}
}
复制代码
接下来就是bean的定义了
@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. 接口定义
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
public class LoadIfBeanNotExists {public String name;public LoadIfBeanNotExists(String name) {this.name = name;}public String getName() {return "load if bean not exists: " + name;}
}
复制代码
对应的bean配置如下
/*** 只有当没有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. 注解定义
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
public class DependedClz {
}
复制代码
然后依赖class存在的bean
public class LoadIfClzExists {private String name;public LoadIfClzExists(String name) {this.name = name;}public String getName() {return "load if exists clz: " + name;}
}
复制代码
接下来就是Bean的配置
/*** 当引用了 {@link DependedClz} 类之后,才会创建bean: `LoadIfClzExists`** @return*/
@Bean
@ConditionalOnClass(DependedClz.class)
public LoadIfClzExists loadIfClzExists() {return new LoadIfClzExists("dependedClz");
}
复制代码
因为类存在,所以测试时,这个bean应该被正常注册
2. @ConditionalOnMissingClass
class不存在时,才会加载bean
a. 注解定义
public @interface ConditionalOnMissingClass {String[] value() default {};
}
复制代码
b. 测试用例
定义一个class缺少时才会创建的bean
public class LoadIfClzNotExists {private String name;public LoadIfClzNotExists(String name) {this.name = name;}public String getName() {return "load if not exists clz: " + name;}
}
复制代码
bean的配置如下
/*** 当系统中没有 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是否正常
@RestController
@RequestMapping("depends")
public class DependRest {@Autowiredprivate LoadIfBeanExist loadIfBeanExist;@Autowiredprivate LoadIfBeanNotExists loadIfBeanNotExists;@Autowiredprivate 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. 配置属性作为条件
主要是根据配置参数,来决定是否需要创建这个bean,这样就给了我们一个根据配置来控制Bean的选择的手段了,如前面一篇博文中根据配置来选择是随机生成boolean还是随机生成int;只需要更改配置即可
1. @ConditionalOnProperty
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {/*** Alias for {@link #name()}.* @return the names*/String[] value() default {};// 配置前缀String prefix() default "";// 配置名String[] name() default {};// 要求配置存在,且包含某个值String havingValue() default "";// 即便没有配置,也依然创建boolean matchIfMissing() default false;
}
复制代码
2. 实例测试
a. 测试用例
测试几个常用的姿势,一是根据配置是否存在,来决定是否创建
public class PropertyExistBean {private String name;public PropertyExistBean(String name) {this.name = name;}public String getName() {return "property : " + name;}
}public class PropertyNotExistBean {private String name;public PropertyNotExistBean(String name) {this.name = name;}public String getName() {return "no property" + name;}
}
复制代码
对应的bean配置如下
/*** 配置存在时才会加载这个bean** @return*/
@Bean
@ConditionalOnProperty("conditional.property")
public PropertyExistBean propertyExistBean() {return new PropertyExistBean(environment.getProperty("conditional.property"));
}/*** 即便配置不存在时,也可以加载这个bean** @return*/
@Bean
@ConditionalOnProperty(name = "conditional.property.no", matchIfMissing = true)
public PropertyNotExistBean propertyNotExistBean() {return new PropertyNotExistBean("conditional.property");
}
复制代码
当配置存在,且value匹配时
public class PropertyValueExistBean {public String name;public PropertyValueExistBean(String name) {this.name = name;}public String getName() {return "property value exist: " + name;}
}public class PropertyValueNotExistBean {public String name;public PropertyValueNotExistBean(String name) {this.name = name;}public String getName() {return "property value not exist: " + name;}
}
复制代码
对应的配置如下
@Bean
@ConditionalOnProperty(name = {"conditional.property"}, havingValue = "properExists")
public PropertyValueExistBean propertyValueExistBean() {return new PropertyValueExistBean("properExists");
}@Bean
@ConditionalOnProperty(name = {"conditional.property"}, havingValue = "properNotExists")
public PropertyValueNotExistBean propertyValueNotExistBean() {return new PropertyValueNotExistBean("properNotExists");
}
复制代码
接下来就是配置的参数
conditional.property=properExists
复制代码
b. 实例演示
根据前面的分析,上面的四个bean中,PropertyExistBean
, PropertyNotExistBean
, PropertyValueExistBean
应该存在;而PropertyValueNotExistBean
因为配置值不匹配,不会创建
测试代码如下
@RestController
@RequestMapping(path = "property")
public class PropertyRest {@Autowired(required = false)private PropertyExistBean propertyExistBean;@Autowired(required = false)private PropertyNotExistBean propertyNotExistBean;@Autowired(required = false)private PropertyValueExistBean propertyValueExistBean;@Autowired(required = false)private PropertyValueNotExistBean propertyValueNotExistBean;@GetMapping(path = "show")public String show() {Map<String, String> result = new HashMap<>(4);// 存在result.put("propertyExistBean", propertyExistBean == null ? "null ===> false" : propertyExistBean.getName());// 存在result.put("propertyNotExistBean",propertyNotExistBean == null ? "null ===> false" : propertyNotExistBean.getName());// 存在result.put("propertyValueExistBean",propertyValueExistBean == null ? "null ==> false" : propertyValueExistBean.getName());// 不存在result.put("propertyValueNotExistBean",propertyValueNotExistBean == null ? "null ==> true" : propertyValueNotExistBean.getName());return JSONObject.toJSONString(result);}
}
复制代码
执行后结果如下,一如预期
IV. 表达式方式
相比较前面的Bean,Class是否存在,配置参数是否存在或者有某个值而言,这个依赖SPEL表达式的,就显得更加的高级了;其主要就是执行Spel表达式,根据返回的true/false来判断是否满足条件
至于SPEL是什么东西,后面会有专文进行解释,此处不加以展开。下面以一个简单的demo进行演示它的使用姿势
1. @ConditionalOnExpression
接口定义
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnExpressionCondition.class)
public @interface ConditionalOnExpression {/*** The SpEL expression to evaluate. Expression should return {@code true} if the* condition passes or {@code false} if it fails.* @return the SpEL expression*/String value() default "true";
}
复制代码
2. 实例测试
用一个简单的例子,当配置参数中,根据是否满足某个条件来决定是否需要加载bean
a. 测试用例
定义一个满足条件和一个不满足的bean
public class ExpressFalseBean {private String name;public ExpressFalseBean(String name) {this.name = name;}public String getName() {return "express bean :" + name;}
}public class ExpressTrueBean {private String name;public ExpressTrueBean(String name) {this.name = name;}public String getName() {return "express bean :" + name;}
}
复制代码
重点关注下bean的配置
@Configuration
public class ExpressAutoConfig {/*** 当存在配置,且配置为true时才创建这个bean* @return*/@Bean@ConditionalOnExpression("#{'true'.equals(environment['conditional.express'])}")public ExpressTrueBean expressTrueBean() {return new ExpressTrueBean("express true");}/*** 配置不存在,或配置的值不是true时,才创建bean* @return*/@Bean@ConditionalOnExpression("#{!'true'.equals(environment.getProperty('conditional.express'))}")public ExpressFalseBean expressFalseBean() {return new ExpressFalseBean("express != true");}
}
复制代码
对应的配置如下
conditional.express=true
复制代码
b. 实例演示
@RestController
@RequestMapping(path = "express")
public class ExpressRest {@Autowired(required = false)private ExpressTrueBean expressTrueBean;@Autowired(required = false)private ExpressFalseBean expressFalseBean;@GetMapping(path = "show")public String show() {Map<String, String> result = new HashMap<>(4);result.put("expressTrueBean", expressTrueBean == null ? "null ==> false" : expressTrueBean.getName());result.put("expressFalseBean", expressFalseBean == null ? "null ==> true": expressFalseBean.getName());return JSONObject.toJSONString(result);}
}
复制代码
上面的执行,expressTrueBean
应该存在,另外一个为null,运行结果如下
III. 其他
0. 相关
a. 更多博文
基础篇
- 181009-SpringBoot基础篇Bean之基本定义与使用
- 181012-SpringBoot基础篇Bean之自动加载
- 181013-SpringBoot基础篇Bean之动态注册
- 181018-SpringBoot基础篇Bean之条件注入@Condition使用姿势
- 181019-SpringBoot基础篇Bean之@ConditionalOnBean与@ConditionalOnClass
- 181019-SpringBoot基础篇Bean之条件注入@ConditionalOnProperty
- 181019-SpringBoot基础篇Bean之条件注入@ConditionalOnExpression
应用篇
- 181017-SpringBoot应用篇Bean之注销与动态注册实现服务mock
b. 项目源码
- 工程:spring-boot-demo
- module: 007-conditionbean
1. 一灰灰Blog
- 一灰灰Blog个人博客 blog.hhui.top
- 一灰灰Blog-Spring专题博客 spring.hhui.top
一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
2. 声明
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
3. 扫描关注
一灰灰blog
SpringBoot基础篇Bean之条件注入之注解使用相关推荐
- SpringBoot基础篇Bean之基本定义与使用
更多Spring文章,欢迎点击 一灰灰Blog-Spring专题 我们知道在Spring中,有两个非常有名的特性,依赖注入(DI)与切面(AOP),其中依赖注入其主要的作用,可以说就是维护Spring ...
- 动力节点王鹤SpringBoot3学习笔记——第二章 掌握SpringBoot基础篇
目录 二.掌控SpringBoot基础篇 2.1 Spring Boot ? 2.1.1 与Spring关系 2.1.2 与SpringCloud关系 2.1.3 最新的Spring Boot3 新 ...
- SpringBoot SpringBoot 基础篇(第一篇) 第2章 SpringBoot 全局配置 2.2 yaml 文件
SpringBoot [千锋教育java教程SpringBoot2全套,springboot快速入门到项目实战视频教程] SpringBoot 基础篇(第一篇) 第2章 SpringBoot 全局配置 ...
- 黑马SpringBoot --基础篇
目录 导读 1.快速上手 1.1SpringBoot入门程序开发 1.1.1基础创建方式--常规 1.1.2手动创建方式--补充 1.1.3基础创建方式--下载 1.1.4基础创建方式--阿里云 1. ...
- SpringBoot基础篇
配置maven file-setting-build-build tool 将项目配置成可执行jar 1打包 mavenProject-lifecircle-package 2寻找 target 3执 ...
- SpringBoot基础篇AOP之基本使用姿势小结
原文:190301-SpringBoot基础篇AOP之基本使用姿势小结 一般来讲,谈到Spring的特性,绕不过去的就是DI(依赖注入)和AOP(切面),在将bean的系列中,说了DI的多种使用姿势: ...
- SpringBoot之Bean之条件注入@ConditionalOnProperty
1.美图 2.配置属性作为条件 主要是根据配置参数,来决定是否需要创建这个bean,这样就给了我们一个根据配置来控制Bean的选择的手段了,如前面一篇博文中根据配置来选择是随机生成boolean还是随 ...
- SpringBoot之Bean之条件注入@ConditionalOnExpression
1.美图 2.概述 ConditionalOnExpression 是基于SPEL表达式的条件注解. 相比较前面的Bean,Class是否存在,配置参数是否存在或者有某个值而言,这个依赖SPEL表达式 ...
- SpringBoot之Bean之条件注入@Condition
1.美图 2.概述 这个注解在Spring4中引入,其主要作用就是判断条件是否满足,从而决定是否初始化并向容器注册Bean 3.定义 @Conditional注解定义如下,其内部主要就是利用了Cond ...
最新文章
- 使用Ext Form自动绑定Html中的Form元素
- mysql sql decode函数用法_oracle中的decode的使用介绍
- GROMACS运行参数之md.mdp文件详解
- python3.7安装turtle步骤-Python怎么引入turtle
- 逆向工具之IDA的使用
- An Introduction to Hashing in the Era of Machine Learning
- java 日期注解 xml_Spring xml注解+java注解
- 如何修改浏览器服务器时间格式,浏览器模式怎么改模式
- 拨盘Demo大赛,获奖公布-20170710
- 安装vuejs全过程、淘宝镜像
- Android官方开发文档Training系列课程中文版:使用Fragment构建动态UI之Fragment创建
- could not find driver和PDO drivers = no value
- echarts 其他样式 折线 重叠_echarts 折线图 areaStyle颜色重叠问题
- 中石化shell_中石化壳牌(江苏)加油站百日攻坚创效分享
- linux c代码调试工具,在 Linux 中调试 C 程序的福音——gdb
- 微信小程序 css边框阴影,微信小程序|CSS的内边距和圆框
- linux虚拟机cpu一分钟内负载,虚拟机性能调优-CPU篇
- Pazera Free Audio Extractor 中文版 - 轻松将视频背景音乐/对话音频提取出来的免费软件...
- 桌面计算机最小化,电脑不显示最小化窗口怎么办win7
- .mdf数据库恢复mysql_恢复mdf文件到数据库方法
热门文章
- 旋转数组的最小数 php 牛客网_一文搞定—移掉K个数字amp;amp;旋转数组amp;amp;全排列...
- 用原生JavaScript实现无缝轮播
- ORA-28000: the account is locked 解决方法
- JavaScript面向对象——深入理解原型继承
- vue2.0+stylus实现星级评定组件,computed计算属性实现全星半星,动态改变星级,多种星星规格
- 【转】c++ http下载文件
- Ubuntu 16.04 下部署Node.js+MySQL微信小程序商城
- 【ZZ】大数据架构师基础:hadoop家族,Cloudera系列产品介绍
- JSP第十四次课:JSP项目开发高级操作2---在线编辑器应用及前台首页显示商品
- PHP 循环删除无限分类子节点