目录
一、@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 {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata annotatedTypeMetadata) {Environment environment = context.getEnvironment();String flagString = environment.getProperty("enable.flag");// 如果flagString为true,则直接返回trueif(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;}@Overridepublic String toString() {return name + "是TestBean";}
}
3.1.4 Config类
@Conditonal注解作用在类上,并且判断的条件只有一个
@Configuration
@Conditional(TestCondition.class)
public class TestConfig {@Beanpublic 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 {@Overridepublic 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 {@Beanpublic 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;}@Overridepublic String toString() {return "测试方法Bean";}
}
3.3.2 Condition实现类
这个实现类直接返回结果直接为false,用于测试
public class TestCondition3 implements Condition {@Overridepublic 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 {@Autowiredprivate 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 {@Autowiredprivate 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 {@Autowiredprivate 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 {@Beanpublic 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这个Beanpublic TestBean2 testBean4(){TestBean2 testBean2 = new TestBean2();testBean2.setAddress("北京");return testBean2;}
}
4.2.3.2 Controller
@RestController
public class TestController {@Autowiredprivate 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;}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = (beanFactory instanceof ConfigurableListableBeanFactory)? (ConfigurableListableBeanFactory) beanFactory : null;}@Overridepublic 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 {@Autowiredprivate 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应用就会注入beanpublic TestBean testBean(){TestBean testBean = new TestBean();testBean.setName("java");testBean.toString();return testBean;}@Bean@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) //web应用类型是reactive应用就会注入beanpublic TestBean testBean2(){TestBean testBean = new TestBean();testBean.setName("java");testBean.toString();return testBean;}
}
4.2.5.2 Controller
@RestController
public class TestController {@Autowiredprivate 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")@Beanpublic TestBean testBean2(){TestBean testBean = new TestBean();testBean.setName("java");testBean.toString();return testBean;}
}
4.2.6.3 Contoller
@RestController
public class TestController {@Autowiredprivate 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对应的配置在配置文件不存在,所以失败了