目录
一、@Conditional注解作用
二、@Conditional源码解析
2.1 Conditional源码
2.2 Condition源码
三、Conditional案例
3.1 Conditional作用在类上案例
3.1.1 配置文件
3.1.2 Condition实现类
3.1.3 Bean内容类
3.1.4 Config类
3.1.5 Controller类
3.1.6 测试结果
3.2 Condition作用在类上且多个条件
3.2.1 增加一个Condition实现类
3.2.2 Config2类
3.3 @Conditional注入到方法上
3.3.1 Bean内容类
3.3.2 Condition实现类
3.3.3 Config配置类
3.3.4 Controller测试类
3.3.5 测试结果
四、@Conditional系列注解拓展
4.1 @Component容器和@Bean容器的区别
4.2 @Conditonal注解拓展
4.2.1 @ConditionalOnClass
4.2.1.1 config类
4.2.1.2 Controller类
4.2.1.3 结果
4.2.2 @ConditionalOnMissingClass
4.2.2.1 Config
4.2.2.2 Controller
4.2.2.3 测试
4.2.3 @ConditionalOnBean
4.2.3.1 config
4.2.3.2 Controller
4.2.3.3 测试结果
4.2.4 @ConditionalOnSingleCandidate
4.2.4.1 config
4.2.4.2 Controller
4.2.4.3 测试结果
4.2.5 @ConditionalOnWebApplication
4.2.5.1 config
4.2.5.2 Controller
4.2.5.3 结果
4.2.6 @ConditionalOnProperty
4.2.6.1 yml文件内容
4.2.6.2 config
4.2.6.3 Contoller
4.2.6.4 测试结果
一、@Conditional注解作用
Conditional中文翻译是条件的意思,@Conditional注解的作用是按照一定的条件进行判断,满足条件给容器注册bean。
二、@Conditional源码解析
2.1 Conditional源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
从源码中可以看到,@Conditional只有一个数组参数,也就是说明,传递的参数是可以多个的,但这个参数是要求继承Condition类。
2.2 Condition源码
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
Condition是一个函数式接口,matches就是它的比较方法,如果为true,那么就注入,如果为false,则不注入
三、Conditional案例
3.1 Conditional作用在类上案例
3.1.1 配置文件
enable:
flag: true
3.1.2 Condition实现类
public class TestCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = context.getEnvironment();
String flagString = environment.getProperty("enable.flag");
// 如果flagString为true,则直接返回true
if(Boolean.parseBoolean(flagString)){
return true;
}
return false;
}
}
3.1.3 Bean内容类
public class TestBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name + "是TestBean";
}
}
3.1.4 Config类
@Conditonal注解作用在类上,并且判断的条件只有一个
@Configuration
@Conditional(TestCondition.class)
public class TestConfig {
@Bean
public TestBean testBean(){
TestBean testBean = new TestBean();
testBean.setName("java");
testBean.toString();
return testBean;
}
}
3.1.5 Controller类
controller用于测试
注意点:@Autowired注解后面建议加上required=false,这样就算Condition实现类(对应上面的TestCondition类)返回的结果为false,那么项目也不会启动报错,如果没有加上,当Condition实现类返回为false的时候,项目启动过程会报Field config in com.common.commonframework.TestController required a bean of type 'com.common.commonframework.TestConfig' that could not be found.错误
@RestController
public class TestController {
//如果为false,启动的时候在这一步会报错,可以添加成
@Autowired(required = false)
private TestConfig config;
@GetMapping("/test")
public void printImportBeanInfo() {
System.out.println(config);
System.out.println(config.testBean());
}
}
3.1.6 测试结果
3.2 Condition作用在类上且多个条件
3.2.1 增加一个Condition实现类
public class TestCondition2 implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//直接返回false,用于测试
return false;
}
}
3.2.2 Config2类
@Conditional多个条件
注意点:下面的代码由于TestConditon2知己诶返回的是false,所以是一定会注入失败的
// TestCondition为false,所以在这个地方是一定会注入失败的
@Configuration
@Conditional({TestCondition.class,TestCondition2.class})
public class TestConfig2 {
@Bean
public TestBean testBean(){
return new TestBean();
}
}
3.3 @Conditional注入到方法上
3.3.1 Bean内容类
public class TestBean2 {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "测试方法Bean";
}
}
3.3.2 Condition实现类
这个实现类直接返回结果直接为false,用于测试
public class TestCondition3 implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return false;
}
}
3.3.3 Config配置类
@Configuration
public class TestConfig3 {
@Bean
@Conditional({TestCondition3.class})
public TestBean2 testBean3(){
TestBean2 testBean2 = new TestBean2();
testBean2.setAddress("上海");
return testBean2;
}
}
3.3.4 Controller测试类
@RestController
public class TestController {
@Autowired
private TestConfig3 config3;
@GetMapping("/test")
public void printImportBeanInfo() {
//因为@Conditional修饰的是在方法上,,所以TestConfig3是会注入进去的
System.out.println(config3);
System.out.println(config3.testBean3());
}
}
3.3.5 测试结果
由于在TestConfig3的方法上面修饰的@Conditonal注解返回的是false,所以我们在调用对应的Bean的时候会直接报"No bean named ''xx" available"错误
四、@Conditional系列注解拓展
springboot提供的@Conditonal系列注解作用:对@Component,@Bean等容器注解进行校验。校验是否满足指定的条件,只有满足指定的条件时,才会将对应的内容放到容器里面。如果在启动过程当中,直接报了@Bean容器重复,那么建议直接用@Conditional注解处理。
4.1 @Component容器和@Bean容器的区别
@Component作用于类,@Bean作用于方法
@Component是通过路劲扫描的方式自动装配到bean容器中,而@Bean是将方法返回值作为bean自动装配到IOC容器中
@Bean功能比@Component的功能更加强大,当我们需要引用外部类,并需要将它注入到IOC容器时,@Component注解是做不到的,但@Bean可以做到。
4.2 @Conditonal注解拓展
4.2.1 @ConditionalOnClass
当给定的类名在类路径上存在,则实例化当前Bean
4.2.1.1 config类
TestBean2类在文章的上面
@Configuration
public class TestConfig4 {
@Bean
@ConditionalOnClass(TestBean2.class) //当TestBean2存在,那么这个bean就会注入到容器
public TestBean2 testBean3(){
TestBean2 testBean2 = new TestBean2();
testBean2.setAddress("北京");
return testBean2;
}
@Bean
@ConditionalOnClass(name = "com.common.commonframework.TestBean2") //也可以通过路径直接获取对应的类
public TestBean2 testBean4(){
TestBean2 testBean2 = new TestBean2();
testBean2.setAddress("北京");
return testBean2;
}
}
4.2.1.2 Controller类
@RestController
public class TestController {
@Autowired
private TestConfig4 config4;
@GetMapping("/test")
public void printImportBeanInfo() {
System.out.println(config4);
System.out.println(config4.testBean3());
System.out.println(config4.testBean4());
}
}
4.2.1.3 结果
因为TestBean2这个类存在,所以成功注入
4.2.2 @ConditionalOnMissingClass
当给定的类名在类路径上不存在,则实例化当前Bean
4.2.2.1 Config
@Configuration
public class TestConfig5 {
@Bean
@ConditionalOnMissingClass({ "com.common.commonframework.TestBean2"}) // 如果不存在TestBean2这个类,就将Bean注入容器,目前存在,所以会失败
public TestBean2 testBean3(){
TestBean2 testBean2 = new TestBean2();
testBean2.setAddress("北京");
return testBean2;
}
}
4.2.2.2 Controller
@RestController
public class TestController {
@Autowired
private TestConfig5 config5;
@GetMapping("/test")
public void printImportBeanInfo() {
System.out.println(config5);
System.out.println(config5.testBean3());
}
}
4.2.2.3 测试
提示失败,因为TestBean2是存在的
4.2.3 @ConditionalOnBean
当给定的在bean存在时,则实例化当前Bean
4.2.3.1 config
@Configuration
public class TestConfig6 {
@Bean
public TestBean testBean(){
TestBean testBean = new TestBean();
testBean.setName("java");
testBean.toString();
return testBean;
}
@Bean
@ConditionalOnBean(name = "testBean") //这个会成功,因为存在testBean这个容器
public TestBean2 testBean3(){
TestBean2 testBean2 = new TestBean2();
testBean2.setAddress("北京");
return testBean2;
}
@Bean
@ConditionalOnBean(name = "testBean2") //这个会失败,因为不存在testBean2这个Bean
public TestBean2 testBean4(){
TestBean2 testBean2 = new TestBean2();
testBean2.setAddress("北京");
return testBean2;
}
}
4.2.3.2 Controller
@RestController
public class TestController {
@Autowired
private TestConfig6 config6;
@GetMapping("/test")
public void printImportBeanInfo() {
System.out.println(config6);
System.out.println(config6.testBean3());
System.out.println(config6.testBean4());
}
}
4.2.3.3 测试结果
testBean3成功,testBean4失败
4.2.4 @ConditionalOnSingleCandidate
只有指定的类已存在于BeanFactroy容器中,且只有一个实例时,才会作为bean放到容器。如果有多个,则指定首选的bean。@ConditionalOnSingleCandidate是@ConditionalOnBean的一种情况,满足前者时一定满足后者,满足后者时不一定满足前者
4.2.4.1 config
下面的案例是自定义bean然后注入到BeanFactory之中,springboot实际上有很多bean在启动过程当中会自动注入BeanFactory,不需要我们手动再去注入BeanFactory,比如我们常用的RedisConnectionFactory等等
@Configuration
public class TestConfig7 implements InitializingBean, BeanFactoryAware {
public ConfigurableListableBeanFactory beanFactory;
@Bean
@ConditionalOnSingleCandidate //自定义的bean注入了BeanFactory所以会成功
public TestBean testBean(){
TestBean testBean = new TestBean();
testBean.setName("java");
testBean.toString();
return testBean;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (beanFactory instanceof ConfigurableListableBeanFactory)
? (ConfigurableListableBeanFactory) beanFactory : null;
}
@Override
public void afterPropertiesSet() throws Exception {
TestBean testBean = new TestBean();
testBean.setName("java");
testBean.toString();
beanFactory.registerSingleton("testBean",testBean);
}
}
4.2.4.2 Controller
@RestController
public class TestController {
@Autowired
private TestConfig7 config7;
@GetMapping("/test")
public void printImportBeanInfo() {
System.out.println(config7);
System.out.println(config7.testBean());
}
}
4.2.4.3 测试结果
4.2.5 @ConditionalOnWebApplication
指定对应的web应用类型,才会做为bean放到容器中
4.2.5.1 config
@Configuration
public class TestConfig8 {
@Bean
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) //web应用类型是servlet应用就会注入bean
public TestBean testBean(){
TestBean testBean = new TestBean();
testBean.setName("java");
testBean.toString();
return testBean;
}
@Bean
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) //web应用类型是reactive应用就会注入bean
public TestBean testBean2(){
TestBean testBean = new TestBean();
testBean.setName("java");
testBean.toString();
return testBean;
}
}
4.2.5.2 Controller
@RestController
public class TestController {
@Autowired
private TestConfig8 config8;
@GetMapping("/test")
public void printImportBeanInfo() {
System.out.println(config8);
System.out.println(config8.testBean());
System.out.println(config8.testBean2());
}
}
4.2.5.3 结果
从结果可以看到一个testBean成功,一个testBean2失败,因为我的应用类型是servlet
4.2.6 @ConditionalOnProperty
yml或者properties文件,只有当配置条件满足要求时,才会放到bean容器
4.2.6.1 yml文件内容
conditional:
test: 1
4.2.6.2 config
@Configuration
public class TestConfig9 {
@Bean
@ConditionalOnProperty("conditional.test")
public TestBean testBean(){
TestBean testBean = new TestBean();
testBean.setName("java");
testBean.toString();
return testBean;
}
@ConditionalOnProperty("conditional.test.test")
@Bean
public TestBean testBean2(){
TestBean testBean = new TestBean();
testBean.setName("java");
testBean.toString();
return testBean;
}
}
4.2.6.3 Contoller
@RestController
public class TestController {
@Autowired
private TestConfig9 config9;
@GetMapping("/test")
public void printImportBeanInfo() {
System.out.println(config9);
System.out.println(config9.testBean());
System.out.println(config9.testBean2());
}
}
4.2.6.4 测试结果
因为testBean对应的配置存在,所以会成功,testBean2对应的配置在配置文件不存在,所以失败了