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

一、概述

1、@Conditional 注解定义

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

2、Condition

我们点进去看后,发现它是一个接口,有一个方法。

  1. @FunctionalInterface
  2. public interface Condition {
  3. boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
  4. }

3、ConditionContext

它持有不少有用的对象,可以用来获取很多系统相关的信息,来丰富条件判断,接口定义如下

  1. public interface ConditionContext {
  2. /**
  3. * 获取Bean定义
  4. */
  5. BeanDefinitionRegistry getRegistry();
  6. /**
  7. * 获取Bean工程,因此就可以获取容器中的所有bean
  8. */
  9. @Nullable
  10. ConfigurableListableBeanFactory getBeanFactory();
  11. /**
  12. * environment 持有所有的配置信息
  13. */
  14. Environment getEnvironment();
  15. /**
  16. * 资源信息
  17. */
  18. ResourceLoader getResourceLoader();
  19. /**
  20. * 类加载信息
  21. */
  22. @Nullable
  23. ClassLoader getClassLoader();
  24. }

二、案例

需求 根据当前系统环境的的不同实例不同的 Bean,比如现在是Mac那就实例一个 Bean,如果是Window系统实例另一个 Bean。

1、SystemBean

首先创建一个Bean类

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. @ToString
  5. public class SystemBean {
  6. /**
  7. * 系统名称
  8. */
  9. private String systemName;
  10. /**
  11. * 系统code
  12. */
  13. private String systemCode;
  14. }

2、通过Configuration配置实例化 Bean

  1. @Slf4j
  2. @Configuration
  3. public class ConditionalConfig {
  4. /**
  5. * 如果 WindowsCondition 的实现方法返回 true,则注入这个 bean
  6. */
  7. @Bean("windows")
  8. @Conditional({WindowsCondition.class})
  9. public SystemBean systemWi() {
  10. log.info("ConditionalConfig方法注入 windows实体");
  11. return new SystemBean("windows系统","002");
  12. }
  13. /**
  14. * 如果 MacCondition 的实现方法返回 true,则注入这个 bean
  15. */
  16. @Bean("mac")
  17. @Conditional({MacCondition.class})
  18. public SystemBean systemMac() {
  19. log.info("ConditionalConfig方法注入 mac实体");
  20. return new SystemBean("Mac ios系统","001");
  21. }
  22. }

3、WindowsCondition 和 MacCondition

这两个类都实现了 Condition 接口, 只有 matches 方法返回 true 才会实例化当前 Bean。

1)WindowsCondition**

  1. @Slf4j
  2. public class WindowsCondition implements Condition {
  3. /**
  4. * @param conditionContext:判断条件能使用的上下文环境
  5. * @param annotatedTypeMetadata:注解所在位置的注释信息
  6. */
  7. @Override
  8. public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
  9. //获取ioc使用的beanFactory
  10. ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
  11. //获取类加载器
  12. ClassLoader classLoader = conditionContext.getClassLoader();
  13. //获取当前环境信息
  14. Environment environment = conditionContext.getEnvironment();
  15. //获取bean定义的注册类
  16. BeanDefinitionRegistry registry = conditionContext.getRegistry();
  17. //获得当前系统名
  18. String property = environment.getProperty("os.name");
  19. //包含Windows则说明是windows系统,返回true
  20. if (property.contains("Windows")){
  21. log.info("当前操作系统是:Windows");
  22. return true;
  23. }
  24. return false;
  25. }
  26. }


2) MacCondition**

  1. @Slf4j
  2. public class MacCondition implements Condition {
  3. @Override
  4. public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
  5. Environment environment = conditionContext.getEnvironment();
  6. String property = environment.getProperty("os.name");
  7. if (property.contains("Mac")) {
  8. log.info("当前操作系统是:Mac OS X");
  9. return true;
  10. }
  11. return false;
  12. }
  13. }

4、测试类测试

  1. /**
  2. * @author xub
  3. * @date 2019/6/13 下午10:42
  4. */
  5. @SpringBootTest(classes = Application.class)
  6. @RunWith(SpringRunner.class)
  7. public class TestConditionOn {
  8. @Autowired
  9. private SystemBean windows;
  10. @Autowired
  11. private SystemBean mac;
  12. @Test
  13. public void test() {
  14. if (windows != null) {
  15. System.out.println("windows = " + windows);
  16. }
  17. if (mac != null) {
  18. System.out.println("linux = " + mac);
  19. }
  20. }
  21. }


运行结果**
01 @Conditional 注解 - 图1
通过运行结果可以看出

  1. 虽然配置两个 Bean,但这里只实例化了一个 Bean,因为我这边是Mac电脑,所以实例化的是 mac 的 SystemBean
  2. 注意一点,我们可以看出 window并不为 null,而是mac 实例化的 Bean。说明只要实例化一个 Bean 的,不管你命名什么,都可以注入这个Bean。


修改一下**
这里做一个修改,我们把ConditionalConfig中的这行代码注释掉。

  1. // @Conditional({WindowsCondition.class})

再运行下代码
01 @Conditional 注解 - 图2
通过运行结果可以看出,配置类的两个 Bean 都已经注入成功了

注意 当同一个对象被注入两次及以上的时候,那么你在使用当前对象的时候,名称一定要是两个 bean 名称的一个,否则报错。比如修改为

  1. @Autowired
  2. private SystemBean windows;
  3. @Autowired
  4. private SystemBean mac;
  5. @Autowired
  6. private SystemBean linux;

在启动发现,报错了。
01 @Conditional 注解 - 图3

意思很明显,就是上面只实例化成功一个 SystemBean 的时候,你取任何名字,反正就是把当前已经实例化的对象注入给你就好了。
但是你现在同时注入了两个 SystemBean,你这个时候有个名称为 linux,它不知道应该注入那个 Bean,所以采用了报错的策略。<br />

GitHub源码 https://github.com/yudiandemingzi/spring-boot-study 项目名称 03-conditional