Spring中@Conditional注解详细讲解及示例

目录

一、概述

二、举例

三 、根据条件注入容器

1. 标注在方法上

2. 标注在类上

3. 多个条件类


Spring学习专栏

1. Spring源码分析(一)基本框架介绍

2. Spring源码分析(二)两种方案搭建基础框架

3. Spring中@Conditional注解详细讲解及示例

4. Spring中@Import注解详细讲解及示例


若本文讲解有描述错误之处,或者有错别字,欢迎指正,希望大家毫不吝啬。

一、概述

@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。

@Conditional的定义

//此注解可以标注在类和方法上
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME) 
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}

从代码中可以看到,需要传入一个Class数组,并且需要继承Condition接口:

public interface Condition {
    boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}

Condition是个接口,需要实现matches方法,返回true则注入bean,false则不注入

 

二、举例

1. 创建 PersonEntity类

public class PersonEntity {

    private String userName;
    private Integer age;

    public PersonEntity() { }

    public PersonEntity(String userName, Integer age) {
        this.userName = userName;
        this.age = age;
    }

    @Override
    public String toString() {
        return "PersonEntity{" +
                "userName='" + userName + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}

 

2. 创建BeanConfiguration类

   用于配置两个 PersonEntity 实例并注入,一个是比尔盖茨,一个是林纳斯

@Configuration
public class BeanConfiguration {

    @Bean(name = "bill")
    protected PersonEntity getBillInfo(){

        return new PersonEntity("bill",38);
    }

    @Bean("linus")
    protected PersonEntity getBinusInfo(){

        return new PersonEntity("linus",58);
    }

}

 

3. 测试类进行验证这两个Bean是否注入成功

public class ConditionalTest {

    private static AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfiguration.class);


    @Test
    public void test1(){

        Map<String, PersonEntity> map = applicationContext.getBeansOfType(PersonEntity.class);
        System.out.println(map);

    }
}

 运行,输出结果是这样的,两个PersonEntity实例被注入进容器

三 、根据条件注入容器

这是一个简单的例子,现在问题来了,如果我想根据当前操作系统来注入PersonEntity实例Windows下注入bill,Linux下注入linus,怎么实现呢?

这就需要我们用到@Conditional注解了,前言中提到,需要实现Condition接口,并重写方法来自定义match规则。

1. 创建一个WindowsCondition类

public class WindowsCondition implements Condition {

    /**
     * @param conditionContext:判断条件能使用的上下文环境
     * @param annotatedTypeMetadata:注解所在位置的注释信息
     * */
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        //获取ioc使用的beanFactory
//        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
        //获取类加载器
//        ClassLoader classLoader = conditionContext.getClassLoader();
        //获取bean定义的注册类
//        BeanDefinitionRegistry registry = conditionContext.getRegistry();

        //获取当前环境信息
        Environment environment = conditionContext.getEnvironment();
        //获得当前系统环境
        String property = environment.getProperty("os.name");
        //包含Windows则说明是windows系统,返回true
        if (property.contains("Windows")){
            return true;
        }
        return false;
    }
}

 matches方法的两个参数的意思在注释中讲述了,值得一提的是,conditionContext提供了多种方法,方便获取各种信息,也是SpringBoot中 @ConditonalOnxx注解多样扩展的基础。

2. 创建LinuxCondition类

public class LinuxCondition implements Condition {

    /**
     * @param conditionContext:判断条件能使用的上下文环境
     * @param annotatedTypeMetadata:注解所在位置的注释信息
     * */
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {

        // 获取当前环境信息
        Environment environment = conditionContext.getEnvironment();
        // 获得当前系统环境
        String property = environment.getProperty("os.name");
        // 包含Windows则说明是windows系统,返回true
        if (property.contains("Linux")){
            return true;
        }
        // 放弃注入
        return false;
    }
}

 

3.测试条件注入容器

接着就是使用这两个类了,因为此注解可以标注在方法上和类上,所以分开测试

1. 标注在方法上

修改BeanConfig

@Configuration
public class BeanConfiguration {

    //如果WindowsCondition的实现方法返回true,则注入这个bean
    @Conditional(WindowsCondition.class)
    @Bean(name = "bill")
    protected PersonEntity getBillInfo(){

        return new PersonEntity("bill",38);
    }

    @Conditional(LinuxCondition.class)
    @Bean("linus")
    protected PersonEntity getBinusInfo(){

        return new PersonEntity("linus",58);
    }

}

 

修改测试方法,使其可以打印当前系统名:

 @Test
    public void test1(){
        
        String osName = applicationContext.getEnvironment().getProperty("os.name");
        System.out.println("当前系统为:" + osName);
        Map<String, PersonEntity> map = applicationContext.getBeansOfType(PersonEntity.class);
        System.out.println(map);

    }

 

运行结果如下

我是运行在windows上的所以只注入了bill,嗯,没毛病!

接着实验linux下的情况,不能运行在linux下,但可以修改运行时参数:

-Dos.name=Linux

 

修改后启动测试方法

 一个方法只能注入一个bean实例,所以@Conditional标注在方法上只能控制一个Bean实例是否注入

2. 标注在类上

一个类中可以注入很多实例,@Conditional标注在类上就决定了一批Bean是否注入。

我们试一下,将BeanConfiguration改写,这时,如果WindowsCondition返回true,则两个PersonEntity实例将被注入(注意:上一个测试将os.name改为linux,这是我将把这个参数去掉):

@Conditional(WindowsCondition.class)
@Configuration
public class BeanConfiguration {


    @Bean(name = "bill")
    protected PersonEntity getBillInfo(){

        return new PersonEntity("bill",38);
    }

    @Bean("linus")
    protected PersonEntity getBinusInfo(){

        return new PersonEntity("linus",58);
    }

}

 

结果两个实例都被注入

如果将类上的WindowsCondition.class改为LinuxCondition.class,结果应该可以猜到:

结果就是空的,类中所有bean都没有注入

3. 多个条件类

前言中说,@Conditional 注解传入的是一个Class数组,存在多种条件类的情况。

这种情况貌似判断难度加深了,测试一波,新增新的条件类,实现的matches返回false(这种写死返回false的方法纯属测试用,没有实际意义O(∩_∩)O)

public class ObstinateCondition implements Condition {

    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return false;
    }
}

BeanConfiguration修改一下

@Conditional({WindowsCondition.class,ObstinateCondition.class})
@Configuration
public class BeanConfiguration {


    @Bean(name = "bill")
    protected PersonEntity getBillInfo(){

        return new PersonEntity("bill",38);
    }

    @Bean("linus")
    protected PersonEntity getBinusInfo(){

        return new PersonEntity("linus",58);
    }

}

控制台输入

现在如果将ObstinateCondition的matches方法返回值改成true,两个bean就被注入进容器:

结论

第一个条件类实现的方法返回true,第二个返回false,则结果false,不注入进容器。

第一个条件类实现的方法返回true,第二个返回true,则结果true,注入进容器中。

 

项目地址

https://gitee.com/gaibianzlp/spring-study-demo.git

 

参考文档

1. Spring @Conditional注解 详细讲解及示例

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Spring Boot,@Conditional注解和@AutoConfigureAfter注解是非常常用的注解,下面我来给你详细解析一下这两个注解。 ## @Conditional注解 @Conditional注解Spring Boot非常重要的一个注解,在Spring Boot,很多自动配置都是通过@Conditional注解来实现的。 @Conditional注解可以根据满足某些条件来决定是否创建一个bean。比如,我们可以根据某个类是否存在来决定是否创建一个bean,具体示例如下: ```java @Configuration @Conditional(ExistClassCondition.class) public class MyConfiguration { @Bean public MyBean myBean() { return new MyBean(); } } public class ExistClassCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { try { Class.forName("com.example.MyClass"); return true; } catch (ClassNotFoundException e) { return false; } } } ``` 上面的代码,我们定义了一个MyConfiguration类,并且在该类上加了@Conditional注解,该注解的参数是一个Condition的实现类ExistClassCondition。ExistClassCondition的matches方法返回true的条件是com.example.MyClass类存在。 这样,当com.example.MyClass类存在的时候,MyBean这个bean才会被创建。否则,MyBean这个bean不会被创建。 ## @AutoConfigureAfter注解 @AutoConfigureAfter注解也是Spring Boot比较常用的注解之一,它可以用来控制自动配置的顺序。 比如,我们可以通过@AutoConfigureAfter注解来控制某个自动配置类在另一个自动配置类之后加载,具体示例如下: ```java @Configuration @AutoConfigureAfter(MyAutoConfiguration.class) public class MyAnotherAutoConfiguration { // ... } ``` 上面的代码,我们定义了一个MyAnotherAutoConfiguration类,并且在该类上加了@AutoConfigureAfter注解,该注解的参数是MyAutoConfiguration.class。这样,在Spring Boot启动时,MyAutoConfiguration这个自动配置类会先于MyAnotherAutoConfiguration这个自动配置类被加载。 总结:@Conditional注解和@AutoConfigureAfter注解都是Spring Boot非常实用的注解。通过@Conditional注解可以实现根据满足某些条件来决定是否创建一个bean,通过@AutoConfigureAfter注解可以控制自动配置类的加载顺序,这些都是我们在实际开发非常常用的功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值