文章目录
写在前面:
我是「境里婆娑」。我还是从前那个少年,没有一丝丝改变,时间只不过是考验,种在心中信念丝毫未减,眼前这个少年,还是最初那张脸,面前再多艰险不退却。
写博客的目的就是分享给大家一起学习交流,如果您对 Java感兴趣,可以关注我,我们一起学习。
前言:Conditional在项目中经常用到,其作用是按照一定的条件进行判断,满足条件给容器注册bean。如果感兴趣的同学可以跟着我一起去一步步去揭开这个注解的面纱。
一、Conditional简介
@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。
@Conditional定义:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition Conditions} that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
从代码中可以看出:
1、这个注解可以作用到类和方法上
2、传入一个Class数组,并且需要继承Condition接口,需要实现matches方法,返回true则注入bean,false则不注入。
二、Conditional用法
1、创建一个car类
/**
* @author shuliangzhao
* @Title: Car
* @ProjectName spring-boot-learn
* @Description: TODO
* @date 2020/11/21 11:08
*/
public class Car {
private String color;
private String brand;
public Car(String color, String brand) {
this.color = color;
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
@Override
public String toString() {
return "Car{" +
"color='" + color + '\'' +
", brand='" + brand + '\'' +
'}';
}
}
2、创建ConditionalConfig类,用于配置两个Card实例并注入
/**
* @author shuliangzhao
* @Title: ConditionConfig
* @ProjectName spring-boot-learn
* @Description: TODO
* @date 2020/11/21 11:10
*/
@Configuration
public class ConditionConfig {
@Bean
public Car car1() {
return new Car("red","奥迪");
}
@Bean
public Car car2() {
return new Car("blue","宝马");
}
}
3、创建一个SpringUtil获取容器bean工具类
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}
4、启动程序查看结果
@SpringBootApplication
public class SpringBootExmpleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootExmpleApplication.class, args);
System.out.println(SpringUtil.getBeansOfType(Car.class));
}
}
运行,输出结果,两个car实例被注入进容器。
Car{color=‘red’, brand=‘奥迪’} Car{color=‘blue’, brand=‘宝马’}
如果我想根据当前操作系统来注入Car实例,windows下注入奥迪,linux下注入宝马,怎么做?
这就需要我们用到@Conditional注解了,前言中提到,需要实现Condition接口,并重写方法来自定义match规则。
创建WindowsCondition类
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
//获取ioc使用的beanFactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//获取类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
//获取当前环境信息
Environment environment = conditionContext.getEnvironment();
//获取bean定义的注册类
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//获得当前系统名
String property = environment.getProperty("os.name");
//包含Windows则说明是windows系统,返回true
if (property.contains("Windows")){
return true;
}
return false;
}
}
创建LinuxCondition类
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
if (property.contains("Linux")){
return true;
}
return false;
}
}
注意:matches方法的conditionContext提供了多种方法,方便获取各种信息。
1、Conditonal注解作用在方法上
@Configuration
public class ConditionConfig {
@Bean
@Conditional({WindowsCondition.class})
public Car car1() {
return new Car("red","奥迪");
}
@Bean
@Conditional({LinuxCondition.class})
public Car car2() {
return new Car("blue","宝马");
}
}
运行结果如下:
当前系统为:Windows 8.1
Car{color=‘red’, brand=‘奥迪’}
修改运行时参数,运行linux
运行结果如下:
当前系统为:Linux
Car{color=‘blue’, brand=‘宝马’}
总结: 一个方法只能注入一个bean实例,所以@Conditional标注在方法上只能控制一个bean实例是否注入。
2、Conditonal注解作用在类上
@Conditional标注在类上就决定了一批bean是否注入。
ConditionConfig改写,如果WindowsCondition返回true,则两个Car实例将被注入(注意:上一个测试将os.name改为linux,这是我将把这个参数去掉):
@Configuration
@Conditional({WindowsCondition.class})
public class ConditionConfig {
@Bean
public Car car1() {
return new Car("red","奥迪");
}
@Bean
public Car car2() {
return new Car("blue","宝马");
}
}
运行结果如下:
当前系统为:Windows 8.1 {car1=Car{color=‘red’, brand=‘奥迪’},
car2=Car{color=‘blue’, brand=‘宝马’}}
如果将类上的WindowsCondition.class改为LinuxCondition.class,运行结果为:{}
3、类上注入多个条件类
@Conditional注解传入的是一个Class数组,存在多种条件类的情况,新增新的条件类,实现的matches返回false
public class TestCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false;
}
}
运行结果为:
当前系统为:Windows 8.1
{}
现在将TestCondition 返回结果改为true
运行结果为:
当前系统为:Windows 8.1 {car1=Car{color=‘red’, brand=‘奥迪’},
car2=Car{color=‘blue’, brand=‘宝马’}}
总结如下:
第一个条件类实现的方法返回true,第二个返回false,则结果false,不注入进容器。
第一个条件类实现的方法返回true,第二个返回true,则结果true,注入进容器中。
三、Conditional扩展
@ConditionalOnBean // 当给定的在bean存在时,则实例化当前Bean
@ConditionalOnMissingBean // 当给定的在bean不存在时,则实例化当前Bean
@ConditionalOnClass // 当给定的类名在类路径上存在,则实例化当前Bean
@ConditionalOnMissingClass // 当给定的类名在类路径上不存在,则实例化当前Bean
如果想详细了解请移步看这篇文章SpringBoot(16)—@ConditionalOnBean与@ConditionalOnClass
到此Spring中的Conditional介绍完毕。如果还有不明白的可以留言。
—————————————————————————————————
由于本人水平有限,难免有不足,恳请各位大佬不吝赐教!