Spring Conditional

Conditional

  1. @Target({ ElementType.TYPE, ElementType.METHOD })
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Conditional {
  5. /**
  6. * 多个匹配器接口
  7. */
  8. Class<? extends Condition>[] value();
  9. }

Condition

  1. @FunctionalInterface
  2. public interface Condition {
  3. /**
  4. * 匹配,如果匹配返回true进行初始化,返回false跳过初始化
  5. */
  6. boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
  7. }
  • ConditionContext 上下文
  • AnnotatedTypeMetadata 注解信息

ConditionContext

  1. public interface ConditionContext {
  2. /**
  3. * bean的定义
  4. */
  5. BeanDefinitionRegistry getRegistry();
  6. /**
  7. * bean 工厂
  8. */
  9. @Nullable
  10. ConfigurableListableBeanFactory getBeanFactory();
  11. /**
  12. * 环境
  13. */
  14. Environment getEnvironment();
  15. /**
  16. * 资源加载器
  17. */
  18. ResourceLoader getResourceLoader();
  19. /**
  20. * 类加载器
  21. */
  22. @Nullable
  23. ClassLoader getClassLoader();
  24. }
  • 唯一实现 : org.springframework.context.annotation.ConditionEvaluator.ConditionContextImpl

  • 构造方法

  1. public ConditionContextImpl(@Nullable BeanDefinitionRegistry registry,
  2. @Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
  3. this.registry = registry;
  4. this.beanFactory = deduceBeanFactory(registry);
  5. this.environment = (environment != null ? environment : deduceEnvironment(registry));
  6. this.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry));
  7. this.classLoader = deduceClassLoader(resourceLoader, this.beanFactory);
  8. }
  • 在构造方法中加载了一些变量, 这些变量是根据一定规则转换后得到. 具体规则不展开.

AnnotatedTypeMetadata

  • 元数据接口
  1. public interface AnnotatedTypeMetadata {
  2. /**
  3. * 获取所有注解
  4. */
  5. MergedAnnotations getAnnotations();
  6. /**
  7. * 是否有注解
  8. */
  9. default boolean isAnnotated(String annotationName) {
  10. return getAnnotations().isPresent(annotationName);
  11. }
  12. /**
  13. * 获取注解的属性
  14. */
  15. @Nullable
  16. default Map<String, Object> getAnnotationAttributes(String annotationName) {
  17. return getAnnotationAttributes(annotationName, false);
  18. }
  19. }

源码分析

  • 对应测试类org.springframework.context.annotation.ConfigurationClassWithConditionTests
  1. @Test
  2. public void conditionalOnMissingBeanMatch() throws Exception {
  3. AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
  4. ctx.register(BeanOneConfiguration.class, BeanTwoConfiguration.class);
  5. ctx.refresh();
  6. assertThat(ctx.containsBean("bean1")).isTrue();
  7. assertThat(ctx.containsBean("bean2")).isFalse();
  8. assertThat(ctx.containsBean("configurationClassWithConditionTests.BeanTwoConfiguration")).isFalse();
  9. }
  10. @Configuration
  11. static class BeanOneConfiguration {
  12. @Bean
  13. public ExampleBean bean1() {
  14. return new ExampleBean();
  15. }
  16. }
  17. @Configuration
  18. @Conditional(NoBeanOneCondition.class)
  19. static class BeanTwoConfiguration {
  20. @Bean
  21. public ExampleBean bean2() {
  22. return new ExampleBean();
  23. }
  24. }
  25. static class NoBeanOneCondition implements Condition {
  26. @Override
  27. public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  28. return !context.getBeanFactory().containsBeanDefinition("bean1");
  29. }
  30. }
  • org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean

shouldSkip

  1. public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
  2. if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
  3. return false;
  4. }
  5. if (phase == null) {
  6. if (metadata instanceof AnnotationMetadata &&
  7. ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
  8. return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
  9. }
  10. return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
  11. }
  12. List<Condition> conditions = new ArrayList<>();
  13. // 获取注解 Conditional 的属性值
  14. for (String[] conditionClasses : getConditionClasses(metadata)) {
  15. for (String conditionClass : conditionClasses) {
  16. // 序列化成注解
  17. Condition condition = getCondition(conditionClass, this.context.getClassLoader());
  18. // 插入注解列表
  19. conditions.add(condition);
  20. }
  21. }
  22. AnnotationAwareOrderComparator.sort(conditions);
  23. for (Condition condition : conditions) {
  24. ConfigurationPhase requiredPhase = null;
  25. if (condition instanceof ConfigurationCondition) {
  26. requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
  27. }
  28. // matches 进行验证
  29. if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
  30. return true;
  31. }
  32. }
  33. return false;
  34. }
  • 读取注解信息, 并且执行注解对应的类的方法

    用例如下. 实例化BeanTwoConfiguration对象的时候会去执行NoBeanOneCondition方法

    ```java @Configuration @Conditional(NoBeanOneCondition.class) static class BeanTwoConfiguration {

    @Bean public ExampleBean bean2() {

    1. return new ExampleBean();

    } }

static class NoBeanOneCondition implements Condition {

  1. @Override
  2. public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  3. return !context.getBeanFactory().containsBeanDefinition("bean1");
  4. }
  5. }
  1. 在开发中可以自定义 matches 规则
  2. 这也是在注册的时候第一个方法
  3. ```java
  4. private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
  5. @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
  6. @Nullable BeanDefinitionCustomizer[] customizers) {
  7. AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
  8. // 和条件注解相关的函数
  9. if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
  10. return;
  11. }
  12. // 省略其他
  13. }