Spring注解之@Conditional

162 阅读3分钟

@Conditional注解用于注册Bean时的条件判断,满足注解的条件则把定义的bean注入到容器中

@Conditional的使用

我们先看下@Conditional的源码

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

   /**
    * All {@link Condition}s that must {@linkplain Condition#matches match}
    * in order for the component to be registered.
    */
   Class<? extends Condition>[] value();

}

从源码我们可以直观的获取到以下信息

  • @Target注解的值是ElementType.TYPEElementType.METHOND,说明该注解可以作用在类和方法上面
  • value需要传入一个class数组,类型是Condition或者Condition的子类,其中Condition是一个接口

下面展示下作用在方法上面的注解,首先是实现Condition的两个子类(根据业务可以有多个实现),接口只有一个方法。方法返回值类型是boolean,当返回值为true时表示满足条件,可以注入到容器中。这里我们让ChinaCondition实现类的方法返回trueEnglishCondition实现类返回false

public class ChinaCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return true;
    }

}
public class EnglishCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return false;
    }

}

业务实现类

public interface Speak {

}
public class EnglishSpeak implements Speak {
    public EnglishSpeak() {
        System.out.println("speak english");
    }
}
public class ChineseSpeak implements Speak {
    public ChineseSpeak() {
        System.out.println("speak chinese");
    }
}

配置类

@Configuration
public class DemoConfig {
    @Conditional({ChinaCondition.class})
    @Bean
    public Speak chineseSpeak() {
        return new ChineseSpeak();
    }

    @Conditional({EnglishCondition.class})
    @Bean
    public Speak englishSpeak() {
        return new EnglishSpeak();
    }
}

测试类

public class ApplicationTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(DemoConfig.class);
        Map<String, Speak> beansOfType = applicationContext.getBeansOfType(Speak.class);
        System.out.println(beansOfType);
    }
}

执行结果:

image.png

从上面执行结果可以看出,EnglishSpeak并没有注入到容器中,因为EnglishCondition实现类中的方法返回的false,不满足条件,所以没有注入到容器中。

注解作用在类上和作用在方法上的使用方式类似,只是作用范围不一样,当满足条件时,配置类中配置的所有的Bean都会注入到容器中。这种方式相比于作用于方法上面的方式粒度比较粗,但是效果都是一样的。

注意:我们看到value是个数组,如果配置了多个conditional则需要所有的都返回true才能将bean注入到容器中,也就是说所有条件之间是且的关系而不是或的关系

@Conditional扩展

@Conditional是最基本的使用方式,需要我们自定义很多实现方式,Spring boot也为我们提供了很多常用的实现,能够让我们开箱即用

  • @ConditionalOnBean:当容器上下文中有指定的bean实例时才会进行实例化
  • @ConditionalOnMissingBean:当容器上下文中没有指定的bean实例时才会进行实例化
  • @ConditionalOnClass:当类路径下有指定的类时才会进行实例化
  • @ConditionalOnMissingClass:当类路径下没有指定的类时才会进行实例化
  • @ConditionalOnJava:当jvm版本为指定的版本范围时才会进行实例化
  • @ConditionalOnResource:当类路径下有指定的资源时才会进行实例化

示例:

  • @ConditionalOnJava(value = JavaVersion.EIGHT,range = ConditionalOnJava.Range.EQUAL_OR_NEWER):当jdk的版本大于等于1.8时,才会实例化该类

  • @ConditionalOnResource(resources = {"dev.properties"}):当类路径下有文件dev.properties时才会实例化该类