返回介绍

Spring 系列

MyBatis

Netty

Dubbo

Tomcat

Redis

Nacos

Sentinel

RocketMQ

番外篇(JDK 1.8)

学习心得

Spring 条件接口

发布于 2024-05-19 21:34:34 字数 8323 浏览 0 评论 0 收藏 0

Spring Conditional

Conditional

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

    /**
     * 多个匹配器接口
     */
    Class<? extends Condition>[] value();

}

Condition

@FunctionalInterface
public interface Condition {

    /**
     * 匹配,如果匹配返回true进行初始化,返回false跳过初始化
     */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}
  • ConditionContext 上下文
  • AnnotatedTypeMetadata 注解信息

ConditionContext

public interface ConditionContext {

   /**
     * bean的定义
    */
   BeanDefinitionRegistry getRegistry();

   /**
     * bean 工厂
    */
   @Nullable
   ConfigurableListableBeanFactory getBeanFactory();

   /**
     * 环境
    */
   Environment getEnvironment();

   /**
     * 资源加载器
    */
   ResourceLoader getResourceLoader();

   /**
     * 类加载器
    */
   @Nullable
   ClassLoader getClassLoader();

}
  • 唯一实现 : org.springframework.context.annotation.ConditionEvaluator.ConditionContextImpl

  • 构造方法

public ConditionContextImpl(@Nullable BeanDefinitionRegistry registry,
      @Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {

   this.registry = registry;
   this.beanFactory = deduceBeanFactory(registry);
   this.environment = (environment != null ? environment : deduceEnvironment(registry));
   this.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry));
   this.classLoader = deduceClassLoader(resourceLoader, this.beanFactory);
}
  • 在构造方法中加载了一些变量, 这些变量是根据一定规则转换后得到. 具体规则不展开.

AnnotatedTypeMetadata

  • 元数据接口
public interface AnnotatedTypeMetadata {

    /**
     * 获取所有注解
     */
    MergedAnnotations getAnnotations();

    /**
     * 是否有注解
     */
    default boolean isAnnotated(String annotationName) {
        return getAnnotations().isPresent(annotationName);
    }

    /**
     * 获取注解的属性
     */
    @Nullable
    default Map<String, Object> getAnnotationAttributes(String annotationName) {
        return getAnnotationAttributes(annotationName, false);
    }

}

源码分析

  • 对应测试类org.springframework.context.annotation.ConfigurationClassWithConditionTests
@Test
public void conditionalOnMissingBeanMatch() throws Exception {
   AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
   ctx.register(BeanOneConfiguration.class, BeanTwoConfiguration.class);
   ctx.refresh();
   assertThat(ctx.containsBean("bean1")).isTrue();
   assertThat(ctx.containsBean("bean2")).isFalse();
   assertThat(ctx.containsBean("configurationClassWithConditionTests.BeanTwoConfiguration")).isFalse();
}



    @Configuration
    static class BeanOneConfiguration {

        @Bean
        public ExampleBean bean1() {
            return new ExampleBean();
        }
    }

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

        @Bean
        public ExampleBean bean2() {
            return new ExampleBean();
        }
    }


    static class NoBeanOneCondition implements Condition {

        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return !context.getBeanFactory().containsBeanDefinition("bean1");
        }
    }
  • org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean

shouldSkip

public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
   if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
      return false;
   }

   if (phase == null) {
      if (metadata instanceof AnnotationMetadata &&
            ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
         return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
      }
      return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
   }

   List<Condition> conditions = new ArrayList<>();
   // 获取注解 Conditional 的属性值
   for (String[] conditionClasses : getConditionClasses(metadata)) {
      for (String conditionClass : conditionClasses) {
         // 序列化成注解
         Condition condition = getCondition(conditionClass, this.context.getClassLoader());
         // 插入注解列表
         conditions.add(condition);
      }
   }

   AnnotationAwareOrderComparator.sort(conditions);

   for (Condition condition : conditions) {
      ConfigurationPhase requiredPhase = null;
      if (condition instanceof ConfigurationCondition) {
         requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
      }

      // matches 进行验证
      if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
         return true;
      }
   }

   return false;
}
  • 读取注解信息, 并且执行注解对应的类的方法

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

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

    @Bean public ExampleBean bean2() {

      return new ExampleBean();
    

    } }

static class NoBeanOneCondition implements Condition {

      @Override
      public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
          return !context.getBeanFactory().containsBeanDefinition("bean1");
      }
  }

在开发中可以自定义 matches 规则

这也是在注册的时候第一个方法

```java
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
      @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
      @Nullable BeanDefinitionCustomizer[] customizers) {

   AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
   // 和条件注解相关的函数
   if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
      return;
   }

    // 省略其他
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文