@Conditional注解使用及@ConditionalOnXXX各注解的作用

本文为博主原创,转载请注明 出处:

一。@Conditional注解作用:

    必须是 @Conditional 注解指定的条件成立,才会在容器中添加组件,配置类里面的所有配置才会生效

二。@Conditional 衍生注解

@Conditional扩展注解作用 判断是否满足指定条件
@ConditionalOnBean 容器中存在指定Bean
@ConditionalOnMissingBean 容器中不存在指定Bean;
@ConditionalOnClass 系统中有指定的类
@ConditionalOnMissingClass 系统中没有指定的类
@ConditionalOnProperty 系统中指定的属性是否有指定的值
@ConditionalOnResource 类路径下是否存在指定资源文件
@ConditionalOnWebApplication 当前是web环境

三。@Conditional注解 的使用 

  3.1 @Conditional 注解源码解析

    @Conditional 注解源码:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}

    通过源码可以发现看到该注解的传参值 为一个数组,并且需要继承 Condition 类。

    查看 Condition 类的源码:

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

    Condition 为一个函数式接口,其中只有一个matches 方法,接口的返回类型为boolean 值,通过该boolean 值控制是否加载配置。

  3.2 @Conditional  实现控制bean 加载

    1. 创建一个bean的实体类:

@Data
@AllArgsConstructor
public class Person {

    private String name;

    private int age;
}

    2. 通过 @Configuration 注解定义两个person 的bean

@Configuration
public class BeanConfiguration {
    @Bean(name= "java")
    public Person person1(){
        return new Person("java",12);
    }
    @Bean(name= "linux")
    public Person person2(){
        return new Person("linux",22);
    }
}

    3. 通过单元测试查看两个bean 

@SpringBootTest
class TestApplicationTests {
    @Autowired
    private ApplicationContext applicationContext;

    @Test
    void contextLoads() {
        Map<String, Person> personBeanMap = applicationContext.getBeansOfType(Person.class);
        personBeanMap.forEach((beanName, beanValue) -> {
            System.out.println(beanName + "--" + beanValue);
        });
    }
}

    通过这个单元测试会打印出两行Person bean 的信息

    4. 定义 实现 Condition 接口的类,并控制是否加载。

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment environment = conditionContext.getEnvironment();
        String property = environment.getProperty("os.name");
        if (property.contains("linux")){
            return true;
        }
        return false;
    }
}

    并在定义Bean 的BeanConfiguration 类中使用 @Condition 注解实现bean 是否加载

import com.example.test.entity.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfiguration {

    @Bean(name= "java")
    public Person person1(){
        return new Person("java",12);
    }

    @Conditional(WindowsCondition.class)
    @Bean(name= "linux")
    public Person person2(){
        return new Person("linux",22);
    }
}

    5.执行单元测试:

import com.example.test.entity.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;

import java.util.Map;

@SpringBootTest
class TestApplicationTests {
    @Autowired
    private ApplicationContext applicationContext;

    @Test
    void contextLoads() {
        Map<String, Person> personBeanMap = applicationContext.getBeansOfType(Person.class);
        personBeanMap.forEach((beanName, beanValue) -> {
            System.out.println(beanName + "--" + beanValue);
        });
    }
}

    运行之后只会打印 person1 这个bean 的信息。

四。@ConditionalOnProperty 注解的使用

    该注解经常用来管理配置文件中的某些配置开关,比如 中间件 通过这个配置判断是否进行加载。

    查看 @ConditionalOnProperty 注解源码

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnPropertyCondition.class})
public @interface ConditionalOnProperty {
    String[] value() default {};

    String prefix() default "";

    String[] name() default {};

    String havingValue() default "";

    boolean matchIfMissing() default false;
}

    value : String数组 该属性与下面的 name 属性不可同时使用,当value所对应配置文件中的值为false时,注入不生效,不为fasle注入生效

    prefix  :配置文件中key的前缀,可与value 或 name 组合使用

      name  :与 value 作用一致

    havingValue  : 与value 或 name 组合使用,只有当value 或 name 对应的值与havingValue的值相同时,注入生效 

     matchIfMissing  :该属性为true时,配置文件中缺少对应的value或name的对应的属性值,也会注入成功

     使用示例:

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnProperty(prefix = "redis.config",value = "enabled",havingValue = "true")
public class RedisConfiguration {
}

    在application.yml 配置文件配置:

redis.config.enabled=true

    当 redis.config.enabled 值为 true时,会加载该类的配置,若只为false,则不加载。

    通过这种方式,可以很好的控制一些功能在不同环境上的开发性和使用性。

 

 

  

posted @ 2021-12-13 23:47  香吧香  阅读(904)  评论(0编辑  收藏  举报