一篇文章教你读懂Spring @Conditional注解

写在前面:
我是「境里婆娑」。我还是从前那个少年,没有一丝丝改变,时间只不过是考验,种在心中信念丝毫未减,眼前这个少年,还是最初那张脸,面前再多艰险不退却。
写博客的目的就是分享给大家一起学习交流,如果您对 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介绍完毕。如果还有不明白的可以留言。
—————————————————————————————————
由于本人水平有限,难免有不足,恳请各位大佬不吝赐教!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

境里婆娑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值