Spring 条件接口详解

举报
龙哥手记 发表于 2022/11/22 15:25:36 2022/11/22
1k+ 0 0
【摘要】 《读尽源码 第三十五篇》

Conditional

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

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

}Copy to clipboardErrorCopied

Condition

@FunctionalInterface
public interface Condition {

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

}Copy to clipboardErrorCopied
  • ConditionContext 上下文
  • AnnotatedTypeMetadata 注解信息

ConditionContext

public interface ConditionContext {

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

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

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

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

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

}Copy to clipboardErrorCopied
  • 唯一实现 : 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);
}Copy to clipboardErrorCopied
  • 在构造方法中加载了一些变量, 这些变量是根据一定规则转换后得到. 具体规则不展开.

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);
    }

}Copy to clipboardErrorCopied

源码分析

  • 对应测试类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");
        }
    }
Copy to clipboardErrorCopied
  • 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;
}Copy to clipboardErrorCopied
  • 读取注解信息, 并且执行注解对应的类的方法

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

    @Configuration
    @Conditional(NoBeanOneCondition.class)
    static class BeanTwoConfiguration {
    
       @Bean
       public ExampleBean bean2() {
          return new ExampleBean();
       }
    }
    
    Copy to clipboardErrorCopied

static class NoBeanOneCondition implements Condition {

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

在开发中可以自定义 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;
 }

  // 省略其他
}
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。