1. 程式人生 > >spring boot 原始碼解析[email protected]

spring boot 原始碼解析[email protected]

前言

之前在分析spring boot 原始碼時匯出可見@ConditionalOnBean 之類的註解,那麼它到底是如何使用的以及其工作流程如何,我們這裡就圍繞以下幾點來分析:

  1. @Conditional系列與Condition的關係
  2. @Conditional與Condition的原始碼分析
  3. 總結

@Conditional系列與Condition的關係

spring boot 中有關Condition的程式碼在org.springframework.boot.autoconfigure.condition中,如圖:

condition所在包截圖

可以看到類還是很多的,但是基本上,都是一個註解對應一個condition實現類.拿其中@ConditionalOnBean,@ConditionalOnClass 為例,其類圖如下:

condition例項類圖

@Conditional與Condition的原始碼分析

  1. 從以上的類圖可以知道,所有的contidon類都是通過繼承SpringBootCondition的方式實現的(實現了Condition介面).Condition介面定義如下:

    public interface Condition {
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
    }

    matches方法判斷其條件是否成立,如果不成立,則會阻止該bean的註冊.

  2. SpringBootCondition實現了Condition介面,將一些模板處理定義在該類中,聲明瞭getMatchOutcome這麼一個抽象方法,子類只需實現該方法即可實現業務邏輯.是模板方法的典型使用.程式碼如下:

    public final boolean matches(ConditionContext context,
            AnnotatedTypeMetadata metadata) {
        // 1. 得到類名或者方法名(條件註解可以作用的類或者方法上)
        String classOrMethodName = getClassOrMethodName(metadata);
    
        // 2. 抽象方法,具體子類實現。ConditionOutcome記錄了匹配結果boolean和log資訊
        ConditionOutcome outcome = getMatchOutcome(context, metadata);
        // 3. 列印日誌,Trace 級別 
    logOutcome(classOrMethodName, outcome); // 4. 記錄結果 recordEvaluation(context, classOrMethodName, outcome); return outcome.isMatch(); }

    4步:

    1. 得到類名或者方法名(條件註解可以作用的類或者方法上).程式碼如下:

      private static String getClassOrMethodName(AnnotatedTypeMetadata metadata) {
      // 1. 如果metadata 是ClassMetadata的例項,則返回類名,否則返回全類名#方法名
      if (metadata instanceof ClassMetadata) {
          ClassMetadata classMetadata = (ClassMetadata) metadata;
          return classMetadata.getClassName();
      }
      MethodMetadata methodMetadata = (MethodMetadata) metadata;
      return methodMetadata.getDeclaringClassName() + "#"
              + methodMetadata.getMethodName();
      }

      如果metadata 是ClassMetadata的例項,則返回類名,否則返回全類名#方法名

    2. 抽象方法,具體子類實現。ConditionOutcome記錄了匹配結果boolean和log資訊
    3. 列印日誌,Trace 級別.程式碼如下:

      protected final void logOutcome(String classOrMethodName, ConditionOutcome outcome) {
      if (this.logger.isTraceEnabled()) {
          this.logger.trace(getLogMessage(classOrMethodName, outcome));
      }
      }
    4. 記錄結果.程式碼如下:

          private void recordEvaluation(ConditionContext context, String classOrMethodName,
          ConditionOutcome outcome) {
      if (context.getBeanFactory() != null) {
          ConditionEvaluationReport.get(context.getBeanFactory())
                  .recordConditionEvaluation(classOrMethodName, this, outcome);
      }
      }
  3. 此外,SpringBootCondition 還聲明瞭2個比較有用的方法,供子類使用:

    1. anyMatches。當有任意一個Condition符號條件時返回true.程式碼如下:

          protected final boolean anyMatches(ConditionContext context,
          AnnotatedTypeMetadata metadata, Condition... conditions) {
      for (Condition condition : conditions) {
          if (matches(context, metadata, condition)) {
              return true;
          }
      }
      return false;
      }
      
    2. matches.程式碼如下:

          protected final boolean matches(ConditionContext context,
          AnnotatedTypeMetadata metadata, Condition condition) {
      if (condition instanceof SpringBootCondition) {
          return ((SpringBootCondition) condition).getMatchOutcome(context, metadata)
                  .isMatch();
      }
      return condition.matches(context, metadata);
      }

      如果Condition是SpringBootCondition的例項,則強轉後呼叫getMatchOutcome進行判斷.然後返回結果.否則直接呼叫matches即可.

  4. 接下來,我們就依次分析org.springframework.boot.autoconfigure.condition中的原始碼.

ConditionalOnBean

  1. @ConditionalOnBean程式碼如下:

    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional(OnBeanCondition.class)
    public @interface ConditionalOnBean {
    
    // bean的型別,當ApplicationContext包含給定類的bean時返回true
    Class<?>[] value() default {};
    
    
    // bean的型別名,當ApplicationContext包含給定的id時返回true   
    String[] type() default {};
    
    // bean所宣告的註解,當ApplicationContext中存在宣告該註解的bean時返回true
    Class<? extends Annotation>[] annotation() default {};
    
    // bean的id,,當ApplicationContext中存在給定id的bean時返回true
    String[] name() default {};
    
    // 預設是所有上下文搜尋
    SearchStrategy search() default SearchStrategy.ALL;
    }

    其中, SearchStrategy是列舉類,其程式碼如下:

    public enum SearchStrategy {
    
    // 查詢當前的context
    CURRENT,
    
    // 查詢所有的祖先和父輩容器,但是不包含當前容器,從1.5開始廢棄,推薦使用ANCESTORS
    @Deprecated PARENTS,
    
    // 搜尋所有的祖先,不搜尋當前的context
    ANCESTORS,
    
    // 搜尋整個上下文
    ALL
    }
  2. @Conditional對應的處理類是OnBeanCondition,其除了繼承SpringBootCondition外,還實現了ConfigurationCondition介面.類圖如下:

    OnBeanCondition類圖

    可以看到OnBeanCondition是@ConditionalOnBean,@ConditionalOnSingleCandidate,@ConditionalOnMissingBean三個註解的處理類,這裡我們只分析@ConditionalOnBean的想關的,其他部分,我們在解析到相關注解時在分析.

    ConfigurationCondition介面定義如下:

    public interface ConfigurationCondition extends Condition {
    
    
    // 返回該condition應該在哪個階段執行
    ConfigurationPhase getConfigurationPhase();
    
    
    
    enum ConfigurationPhase {
    
        // 當前的Condition在配置類解析時執行.如果該condition返回false,則該配置類不會被解析
        PARSE_CONFIGURATION,
    
    
        // 當前的Condition在註冊bean時執行
        REGISTER_BEAN
    }
    }
    

    OnBeanCondition對於ConfigurationCondition的實現如下:

    public ConfigurationPhase getConfigurationPhase() {
        return ConfigurationPhase.REGISTER_BEAN;
    }

    說明該bean是在註冊bean時執行的.

    OnBeanCondition的 getMatchOutcome 實現如下:

    public ConditionOutcome getMatchOutcome(ConditionContext context,
            AnnotatedTypeMetadata metadata) {
        ConditionMessage matchMessage = ConditionMessage.empty();
        if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
            BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
                    ConditionalOnBean.class); // 構造一個BeanSearchSpec,會從@ConditionalOnBean註解中獲取屬性,然後設定到BeanSearchSpec中
            List<String> matching = getMatchingBeans(context, spec);
            if (matching.isEmpty()) {
                // 如果沒有匹配的bean,返回一個沒有匹配成功的ConditionalOutcome
                return ConditionOutcome.noMatch(
                        ConditionMessage.forCondition(ConditionalOnBean.class, spec)
                                .didNotFind("any beans").atAll());
            }
            // 如果找到匹配的bean,匹配資訊進行記錄
            matchMessage = matchMessage.andCondition(ConditionalOnBean.class, spec)
                    .found("bean", "beans").items(Style.QUOTE, matching);
        }
                return ConditionOutcome.match(matchMessage);
    }
    1. 構造一個BeanSearchSpec,會從@ConditionalOnBean註解中獲取屬性,然後設定到BeanSearchSpec中.其構造器如下:

      BeanSearchSpec(ConditionContext context, AnnotatedTypeMetadata metadata,
              Class<?> annotationType) {
          // 1. 對annotationType進行賦值
          this.annotationType = annotationType;
          // 獲得metadata所有的屬性所對應的值,封裝為MultiValueMap,key-->屬性名,value-->所對應的值,class 轉換為String
          MultiValueMap<String, Object> attributes = metadata
                  .getAllAnnotationAttributes(annotationType.getName(), true);
          // 從attributes中提取出name的值,賦值為names
          collect(attributes, "name", this.names);
          // 從attributes中提取出value的值,賦值為value
          collect(attributes, "value", this.types);
          collect(attributes, "type", this.types);
          collect(attributes, "annotation", this.annotations);
          collect(attributes, "ignored", this.ignoredTypes);
          collect(attributes, "ignoredType", this.ignoredTypes);
          // 賦值SearchStrategy
          this.strategy = (SearchStrategy) metadata
                  .getAnnotationAttributes(annotationType.getName()).get("search");
          BeanTypeDeductionException deductionException = null;
          try {
              if (this.types.isEmpty() && this.names.isEmpty()) {
                  // 2. 如果types沒有設定並且names也沒有設定,則如果該metadata是MethodMetadata的例項並且該metadata被@Bean註解
                  // 則將該方法的返回值型別作為types
                  addDeducedBeanType(context, metadata, this.types);
              }
          }
          catch (BeanTypeDeductionException ex) {
              deductionException = ex;
          }
          // 3. 檢驗,如果types,names,annotations 都為空,則丟擲IllegalStateException異常
          validate(deductionException);
      }
      1. 對annotationType進行賦值
      2. 獲得metadata所有的屬性所對應的值,封裝為MultiValueMap,key–>屬性名,value–>所對應的值,class 轉換為String
      3. 呼叫collect方法對names,types,annotations,ignoredTypes,ignoredTypes進行賦值.collect方法從attributes中取出所給定的key的value,進行賦值即可,如果值為String[],則將其強轉為String[]後新增.程式碼如下:

        protected void collect(MultiValueMap<String, Object> attributes, String key,
            List<String> destination) {
        List<?> values = attributes.get(key);
        if (values != null) {
            for (Object value : values) {
                if (value instanceof String[]) {
                    Collections.addAll(destination, (String[]) value);
                }
                else {
                    destination.add((String) value);
                }
            }
        }
        }
      4. 如果types沒有設定並且names也沒有設定,則如果該metadata是MethodMetadata的例項並且該metadata被@Bean註解則將該方法的返回值型別作為types

      5. 檢驗,如果types,names,annotations 都為空,則丟擲IllegalStateException異常
    2. 呼叫getMatchingBeans 獲得匹配的bean的名稱.程式碼如下:

      private List<String> getMatchingBeans(ConditionContext context,
          BeanSearchSpec beans) {
      // 1. 如果搜尋策略為PARENTS或者ANCESTORS,則beanFactory為當前容器的父容器中獲取.否則beanFactory從當前容器獲取
      ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
      if (beans.getStrategy() == SearchStrategy.PARENTS
              || beans.getStrategy() == SearchStrategy.ANCESTORS) {
          BeanFactory parent = beanFactory.getParentBeanFactory();
          Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
                  "Unable to use SearchStrategy.PARENTS");
          beanFactory = (ConfigurableListableBeanFactory) parent;
      }
      // 2. 如果beanFactory等於空,則返回空集合.該情況是對於父容器才會發生的
      if (beanFactory == null) {
          return Collections.emptyList();
      }
      List<String> beanNames = new ArrayList<String>();
      boolean considerHierarchy = beans.getStrategy() != SearchStrategy.CURRENT;
      // 3. 從beanFactory中獲得給定型別的beanIds,如果需要從父容器中搜索,則該方法會合並父容器的介面
      for (String type : beans.getTypes()) {
          beanNames.addAll(getBeanNamesForType(beanFactory, type,
                  context.getClassLoader(), considerHierarchy));
      }
      // 4. 從beanNames刪除給定忽略型別的bean,如果需要從父容器中搜索,則該方法會將父容器中包含給定type的bean刪除
      for (String ignoredType : beans.getIgnoredTypes()) {
          beanNames.removeAll(getBeanNamesForType(beanFactory, ignoredType,
                  context.getClassLoader(), considerHierarchy));
      }
      
      // 5. 遍歷給定的Annotations,依次從beanFactory中獲取聲明瞭該Annotation的bean,如果需要從父容器中搜索,則也會將父容器包含的新增進去
      for (String annotation : beans.getAnnotations()) {
          beanNames.addAll(Arrays.asList(getBeanNamesForAnnotation(beanFactory,
                  annotation, context.getClassLoader(), considerHierarchy)));
      }
      // 6. 遍歷給定的ids,從當前容器和父容器中(如果需要)查詢,如果包含的話,則加入到beanNames
      for (String beanName : beans.getNames()) {
          if (containsBean(beanFactory, beanName, considerHierarchy)) {
              beanNames.add(beanName);
          }
      }
      // 注意,如果同時指定了Types,Names 其返回的結果不是and,而是or
      return beanNames;
      }
      1. 如果搜尋策略為PARENTS或者ANCESTORS,則beanFactory為當前容器的父容器中獲取.否則beanFactory從當前容器獲取
      2. 如果beanFactory等於空,則返回空集合.該情況是對於父容器才會發生的
      3. 從beanFactory中獲得給定型別的beanIds,如果需要從父容器中搜索,則該方法會合並父容器的介面
      4. 從beanNames刪除給定忽略型別的bean,如果需要從父容器中搜索,則該方法會將父容器中包含給定type的bean刪除
      5. 遍歷給定的Annotations,依次從beanFactory中獲取聲明瞭該Annotation的bean,如果需要從父容器中搜索,則也會將父容器包含的新增進去
      6. 遍歷給定的ids,從當前容器和父容器中(如果需要)查詢,如果包含的話,則加入到beanNames

      注意,如果同時指定了Types,Names 其返回的結果不是and,而是or

    3. 如果沒有匹配的bean,返回一個沒有匹配成功的ConditionalOutcome.最終返回false.
    4. 否則,返回匹配.最終返回true.
  3. 使用案例:

    在CacheStatisticsAutoConfiguration類中聲明瞭如下註解:

    @ConditionalOnBean(CacheManager.class)

    標識當 CacheManager型別的bean存在時才對CacheStatisticsAutoConfiguration進行處理.

ConditionalOnSingleCandidate

  1. @ConditionalOnSingleCandidate 程式碼如下:

    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional(OnBeanCondition.class)
    public @interface ConditionalOnSingleCandidate {
    
    /**
     * 
     * bean的型別,當ApplicationContext包含給定類的bean時並且如果有多個該型別的bean並且指定為primary的
     * 存在則返回true.
     * 
     * @return the class type of the bean to check
     */
    Class<?> value() default Object.class;
    
    /**
     * 
     * bean的型別名,當ApplicationContext包含給定的id並且如果有多個該型別的bean並且指定為primary的
     * 存在則返回true.
     * @return the class type name of the bean to check
     */
    String type() default "";
    
    /**
     * 
     * 預設是所有上下文搜尋
     * @return the search strategy
     */
    SearchStrategy search() default SearchStrategy.ALL;
    }

    注意: value ,type 屬性不能同時出現,只能使用一個

  2. 所對應的處理類為OnBeanCondition.程式碼如下:

    if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
            BeanSearchSpec spec = new SingleCandidateBeanSearchSpec(context, metadata,
                    ConditionalOnSingleCandidate.class);
            List<String> matching = getMatchingBeans(context, spec);
            if (matching.isEmpty()) {
                return ConditionOutcome.noMatch(ConditionMessage
                        .forCondition(ConditionalOnSingleCandidate.class, spec)
                        .didNotFind("any beans").atAll());
            }
            else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matching,
                    spec.getStrategy() == SearchStrategy.ALL)) {
                return ConditionOutcome.noMatch(ConditionMessage
                        .forCondition(ConditionalOnSingleCandidate.class, spec)
                        .didNotFind("a primary bean from beans")
                        .items(Style.QUOTE, matching));
            }
            matchMessage = matchMessage
                    .andCondition(ConditionalOnSingleCandidate.class, spec)
                    .found("a primary bean from beans").items(Style.QUOTE, matching);
        }
        return ConditionOutcome.match(matchMessage);
    1. 例項化SingleCandidateBeanSearchSpec,SingleCandidateBeanSearchSpec繼承了BeanSearchSpec.其複寫了validate方法,在該方法中校驗types只能指定一個.同時,複寫了collect方法,這樣在例項化的時候,會去除”“, Object型別的bean.即 @ConditionalOnSingleCandidate 必須指定type,value中的一個,且不能使用預設值 程式碼如下:

      @Override
      protected void collect(MultiValueMap<String, Object> attributes, String key,
              List<String> destination) {
          super.collect(attributes, key, destination);
          destination.removeAll(Arrays.asList("", Object.class.getName()));
      }
      
      @Override
      protected void validate(BeanTypeDeductionException ex) {
          Assert.isTrue(getTypes().size() == 1, annotationName() + " annotations must "
                  + "specify only one type (got " + getTypes() + ")");
      }
    2. 獲得給定type的beanNames

    3. 如果不存在,則返回不匹配
    4. 如果給定型別的bean存在多個但是指定為Primary的不存在,則返回不匹配
    5. 返回匹配
  3. 使用案例:

    在DataSourceTransactionManagerConfiguration 聲明瞭如下註解:

    @Configuration
    @ConditionalOnSingleCandidate(DataSource.class)
    static class DataSourceTransactionManagerConfiguration

    標識:當DataSource型別的bean存在並且指定為Primary的DataSource存在時,載入DataSourceTransactionManagerConfiguration的配置

ConditionalOnMissingBean

  1. @ConditionalOnMissingBean 註解如下:

    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional(OnBeanCondition.class)
    public @interface ConditionalOnMissingBean {
    
    // bean的型別,當ApplicationContext不包含給定類的bean時返回true
    Class<?>[] value() default {};
    
    // bean的型別名,當ApplicationContext不包含給定的id時返回true
    String[] type() default {};
    
    // 給定的型別當進行匹配時進行忽略
    Class<?>[] ignored() default {};
    
    
    // 給定的型別名當進行匹配時進行忽略
    String[] ignoredType() default {};
    
    // bean所宣告的註解,當ApplicationContext中不存在宣告該註解的bean時返回true
    Class<? extends Annotation>[] annotation() default {};
    
    // bean的id,,當ApplicationContext中不存在給定id的bean時返回true
    String[] name() default {};
    
    // 預設是所有上下文搜尋
    SearchStrategy search() default SearchStrategy.ALL;
    }
    
  2. @ConditionalOnMissingBean 對應的處理類是OnBeanCondition,其相關程式碼如下:

    if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
            // 3.1 例項化BeanSearchSpec
            BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
                    ConditionalOnMissingBean.class);
            // 3.2 獲得給定條件的beanNames
            List<String> matching = getMatchingBeans(context, spec);
            if (!matching.isEmpty()) {
                // 3.3 如果不為空,返回不匹配,否則返回匹配
                return ConditionOutcome.noMatch(ConditionMessage
                        .forCondition(ConditionalOnMissingBean.class, spec)
                        .found("bean", "beans").items(Style.QUOTE, matching));
            }
            matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, spec)
                    .didNotFind("any beans").atAll();
        }
        return ConditionOutcome.match(matchMessage);
    1. 例項化BeanSearchSpec
    2. 獲得給定條件的beanNames
    3. 如果不為空,返回不匹配,否則返回匹配
  3. 使用案例:

    在DataSourceAutoConfiguration中聲明瞭如下方法:

    @Bean
    @ConditionalOnMissingBean
    public DataSourceInitializer dataSourceInitializer(DataSourceProperties properties,
            ApplicationContext applicationContext) {
        return new DataSourceInitializer(properties, applicationContext);
    }

    表明當beanFactory中不存在DataSourceInitializer型別的bean時,才進行註冊

ConditionalOnClass與ConditionalOnMissingClass

@ConditionalOnClass與@ConditionalOnMissingClass 對應的處理類都是OnClassCondition.這裡就一起分析了

  1. @ConditionalOnClass註解如下:

    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional(OnClassCondition.class)
    public @interface ConditionalOnClass {
    
    /**
     * 
     * 給定的類必須存在
     * @return the classes that must be present
     */
    Class<?>[] value() default {};
    
    /**
     * 
     * 給定的類名,該類名必須存在
     * @return the class names that must be present.
     */
    String[] name() default {};
    }

    @ConditionalOnMissingClass 註解如下:

    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional(OnClassCondition.class)
    public @interface ConditionalOnMissingClass {
    
    // 給定的類名在當前類路徑下不存在時返回true
    String[] value() default {};
    }
  2. OnClassCondition類圖如下:

    OnClassCondition類圖

    其中AutoConfigurationImportFilter的作用是將在spring.factories中定義的auto-configuration 的類名進行過濾.該介面的目標是快速去除不需要的類在對其配置解析前.一個AutoConfigurationImportFilter介面的實現可能需要實現EnvironmentAware,BeanFactoryAware,BeanClassLoaderAware,ResourceLoaderAware介面中的任意個.這些介面會在呼叫match方法前進行注入.該方法的呼叫鏈如下:

    AutoConfigurationImportFilter方法呼叫鏈

    在AutoConfigurationImportSelector中會載入spring.factories中配置的org.springframework.boot.autoconfigure.AutoConfigurationImportFilter,其配置的剛好就是OnClassCondition.因此該類會在此刻被例項化,進行處理.程式碼如下:

    
    # Auto Configuration Import Filters
    
    org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
    org.springframework.boot.autoconfigure.condition.OnClassCondition
    1. OnClassCondition 中的match 實現如下:

      public boolean[] match(String[] autoConfigurationClasses,
          AutoConfigurationMetadata autoConfigurationMetadata) {
      // 1. 獲得ConditionEvaluationReport
      ConditionEvaluationReport report = getConditionEvaluationReport();
      // 2. 呼叫getOutcomes 獲得ConditionOutcome[]
      ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses,
              autoConfigurationMetadata);
      // 3. 初始化match,該陣列只儲存符合要求的
      boolean[] match = new boolean[outcomes.length];
      // 4. 依次遍歷outcomes
      for (int i = 0; i < outcomes.length; i++) {
          // 4.1 對match中的陣列進行賦值,當outcomes對應下標的ConditionOutcome匹配時為true.其他情況,返回false.
          match[i] = (outcomes[i] == null || outcomes[i].isMatch());
          if (!match[i] && outcomes[i] != null) {
              // 4.2 如果outcome是不滿足的,則列印日誌並進行記錄.
              logOutcome(autoConfigurationClasses[i], outcomes[i]);
              if (report != null) {
                  report.recordConditionEvaluation(autoConfigurationClasses[i], this,
                          outcomes[i]);
              }
          }
      }
      return match;
      }
      1. 獲得ConditionEvaluationReport.該ConditionEvaluationReport只會在beanFactory中例項化1個.
      2. 呼叫getOutcomes 獲得ConditionOutcome[].
      3. 初始化match,該陣列只儲存符合要求的
      4. 依次遍歷outcomes

        1. 對match中的陣列進行賦值,當outcomes等於null 或者 對應下標的ConditionOutcome匹配時為true.其他情況,返回false.一般outcomes都是null.
        2. 如果outcome是不滿足的,則列印日誌並進行記錄.

      其中的核心是第2步–> getOutcomes 方法.程式碼如下:

      private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
          AutoConfigurationMetadata autoConfigurationMetadata) {
          int split = autoConfigurationClasses.length / 2;
          OutcomesResolver firstHalfResolver = createOutcomesResolver(
                  autoConfigurationClasses, 0, split, autoConfigurationMetadata);
          OutcomesResolver secondHalfResolver = new StandardOutcomesResolver(
                  autoConfigurationClasses, split, autoConfigurationClasses.length,
                  autoConfigurationMetadata, this.beanClassLoader);
          ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes();
          ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes();
          ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
          System.arraycopy(firstHalf, 0, outcomes, 0, firstHalf.length);
          System.arraycopy(secondHalf, 0, outcomes, split, secondHalf.length);
          return outcomes;
      }

      這裡有必要說明一下在OnClassCondition中宣告的OutcomesResolver介面:

      private interface OutcomesResolver {
      
      ConditionOutcome[] resolveOutcomes();
      
      }

      該介面就是在第2步–> getOutcomes 中有用到. 實現類有2個:

      1. StandardOutcomesResolver.

        1. 欄位如下:

          // 在META-INFspring.factories/中配置的org.springframework.boot.autoconfigure.EnableAutoConfiguration的類名
          private final String[] autoConfigurationClasses;
          // 處理開始的下標
          private final int start;
          // 處理結束的下標
          private final int end;
          // 自動配置的元資料類,從 META-INF/spring-autoconfigure-metadata.properties
          private final AutoConfigurationMetadata autoConfigurationMetadata;
          // 類載入器
          private final ClassLoader beanClassLoader;
        2. resolveOutcomes 方法如下:

          public ConditionOutcome[] resolveOutcomes() {
          return getOutcomes(this.autoConfigurationClasses, this.start, this.end,
              this.autoConfigurationMetadata);
          }

          呼叫:

          private ConditionOutcome[] getOutcomes(final String[] autoConfigurationClasses,
          int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {
          ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
          for (int i = start; i < end; i++) {
          String autoConfigurationClass = autoConfigurationClasses[i];
          Set<String> candidates = autoConfigurationMetadata
                  .getSet(autoConfigurationClass, "ConditionalOnClass");
          if (candidates != null) {
              outcomes[i - start] = getOutcome(candidates);
          }
          }
          return outcomes;
          }
          1. 例項化ConditionOutcome[],大小為end - start
          2. 遍歷給定的autoConfigurationClasses,依次從autoConfigurationMetadata中獲得通過autoConfigurationClass+”.“+ ConditionalOnClass 所對應的配置(即autoConfigurationClass要生效所需要的類),如果存在的話,則進入第3步
          3. 呼叫getOutcome處理.在該方法最終呼叫了getMatches方法.程式碼如下:

            private List<String> getMatches(Collection<String> candidates, MatchType matchType,
            ClassLoader classLoader) {
            List<String> matches = new ArrayList<String>(candidates.size());
            for (String candidate : candidates) {
            if (matchType.matches(candidate, classLoader)) {
            matches.add(candidate);
            }
            }
            return matches;
            }

            通過遍歷給定的candidates,依次呼叫MatchType#matches方法判斷是否匹配,如果匹配,則加入到matches中.此處使用的是MISSING.其matches最終呼叫isPresent方法.程式碼如下:

            public boolean matches(String className, ClassLoader classLoader) {
            return !isPresent(className, classLoader);
            }
            private static boolean isPresent(String className, ClassLoader classLoader) {
            if (classLoader == null) {
            classLoader = ClassUtils.getDefaultClassLoader();
            }
            try {
            forName(className, classLoader);
            return true;
            }
            catch (Throwable ex) {
            return false;
            }
            }

            通過載入該類的方式進行判斷,如果有不存在,則返回false.(這裡比較繞,仔細想一下就明白了)

      2. ThreadedOutcomesResolver 是對OutcomesResolver的封裝,其欄位如下:

        // 該執行緒負責呼叫OutcomesResolver的resolveOutcomes
        private final Thread thread;
        
        private volatile ConditionOutcome[] outcomes;

        在例項化的時候初始化了Thread,在該執行緒中呼叫OutcomesResolver#resolveOutcomes.如下:

        private ThreadedOutcomesResolver(final OutcomesResolver outcomesResolver) {
        this.thread = new Thread(new Runnable() {
        
            @Override
            public void run() {
                ThreadedOutcomesResolver.this.outcomes = outcomesResolver
                        .resolveOutcomes();
            }
        
        });
        this.thread.start();
        }

        resolveOutcomes實現如下:

        public ConditionOutcome[] resolveOutcomes() {
        try {
            this.thread.join();
        }
        catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
        return this.outcomes;
        }
        

        通過執行緒join的方式,等待outcomesResolver#resolveOutcomes執行完.

    2. OnClassCondition 關於getMatchOutcome的實現如下:

      public ConditionOutcome getMatchOutcome(ConditionContext context,
          AnnotatedTypeMetadata metadata) {
      ClassLoader classLoader = context.getClassLoader();
      ConditionMessage matchMessage = ConditionMessage.empty();
      // 1.1 得到@ConditionalOnClass註解的屬性
      List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
      if (onClasses != null) {
          List<String> missing = getMatches(onClasses, MatchType.MISSING, classLoader);
          if (!missing.isEmpty()) {
              // 1.2. 如果存在類載入器中不存在對應的類,返回一個匹配失敗的ConditionalOutcome
              return ConditionOutcome
                      .noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
                              .didNotFind("required class", "required classes")
                              .items(Style.QUOTE, missing));
          }
          // 1.3 如果類載入器中存在對應的類的話,匹配資訊進行記錄
          matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
                  .found("required class", "required classes").items(Style.QUOTE,
                          getMatches(onClasses, MatchType.PRESENT, classLoader));
      }
      // 對@ConditionalOnMissingClass註解做相同的邏輯處理(說明@ConditionalOnClass和@ConditionalOnMissingClass可以一起使用)
      List<String> onMissingClasses = getCandidates(metadata,
              ConditionalOnMissingClass.class);
      if (onMissingClasses != null) {
          List<String> present = getMatches(onMissingClasses, MatchType.PRESENT,
                  classLoader);
          if (!present.isEmpty()) {
              return ConditionOutcome.noMatch(
                      ConditionMessage.forCondition(ConditionalOnMissingClass.class)
                              .found("unwanted class", "unwanted classes")
                              .items(Style.QUOTE, present));
          }
          matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
                  .didNotFind("unwanted class", "unwanted classes").items(Style.QUOTE,
                          getMatches(onMissingClasses, MatchType.MISSING, classLoader));
      }
      // 返回全部匹配成功的ConditionalOutcome
      return ConditionOutcome.match(matchMessage);
      }
      
      1. 得到@ConditionalOnClass註解的屬性,注意: value和name的屬性可以不一樣,是and的關係
      2. 如果onClasses不為空的話,則呼叫getMatches進行處理,getMatches方法我們之前已經分析過了,如果有給定的類在當前的類路徑上不存在的話,則返回不匹配.否則進行記錄
      3. 得到@ConditionalOnMissingClass註解的屬性.如果不為空的話,則呼叫getMatches進行處理,getMatches方法我們之前已經分析過了,如果有給定的類在當前的類路徑上存在的話,則返回不匹配.否則進行記錄.這裡呼叫的是PRESENT#matches方法.程式碼如下:

        @Override
        public boolean matches(String className, ClassLoader classLoader) {
            return isPresent(className, classLoader);
        }
      4. 最終,返回匹配.
  3. 使用案例:

    1. AopAutoConfiguration聲明瞭如下註解:

      @ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class })

      表明當在當前類路徑存在EnableAspectJAutoProxy.class, Aspect.class, Advice.class時才對AopAutoConfiguration進行解析

    2. Thymeleaf2Configuration 聲明瞭如下註解:

      @ConditionalOnMissingClass("org.thymeleaf.templatemode.TemplateMode")

      表明當在當前類路徑不存在org.thymeleaf.templatemode.TemplateMode時才對Thymeleaf2Configuration進行解析

ConditionalOnCloudPlatform

  1. @ConditionalOnCloudPlatform 程式碼如下:

    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional(OnCloudPlatformCondition.class)
    public @interface ConditionalOnCloudPlatform {
    
        //  給定的CloudPlatform必須是啟用狀態時才返回true
        CloudPlatform value();
    }

    CloudPlatform是一個列舉,其聲明瞭2個方法以供列舉使用:

    1. isUsingForwardHeaders–>表明當前的平臺是否使用X-Forwarded-For這個頭部來進行負載均衡.預設為true.程式碼如下;

      public boolean isUsingForwardHeaders() {
          return true;
      }
    2. getActive–>遍歷CloudPlatform列舉型別,返回一個啟用的CloudPlatform,如果不存在,則返回null.表明不在預設的雲平臺中執行(Cloud Foundry,Heroku). 程式碼如下:

      public static CloudPlatform getActive(Environment environment) {
      if (environment != null) {
          for (CloudPlatform cloudPlatform : values()) {
              if (cloudPlatform.isActive(environment)) {
                  return cloudPlatform;
              }
          }
      }
      return null;
      }
    3. 聲明瞭一個isActive抽象方法–>列舉實現,如果返回true,則表明該spirng boot 應用執行在列舉所對應的雲平臺中.CloudPlatform有2個列舉型別,其實現分別如下:

      1. CLOUD_FOUNDRY–>Cloud Foundry 平臺. 通過判斷當前給定環境變數是否存在VCAP_APPLICATION或者VCAP_SERVICES對應的屬性.程式碼如下:

        CLOUD_FOUNDRY {
            @Override
            public boolean isActive(Environment environment) {
                return environment.containsProperty("VCAP_APPLICATION")
                        || environment.containsProperty("VCAP_SERVICES");
            }
        }
      2. HEROKU–> Heroku 平臺.通過判斷當前給定環境變數是否存在DYNO對應的屬性.程式碼如下:

        HEROKU {
            @Override
            public boolean isActive(Environment environment) {
                return environment.containsProperty("DYNO");
            }
        }
  2. @ConditionalOnCloudPlatform 對應的處理類為OnCloudPlatformCondition.程式碼如下:

    public ConditionOutcome getMatchOutcome(ConditionContext context,
            AnnotatedTypeMetadata metadata) {
        Map<String, Object> attributes = metadata
                .getAnnotationAttributes(ConditionalOnCloudPlatform.class.getName());
        CloudPlatform cloudPlatform = (CloudPlatform) attributes.get("value");
        return getMatchOutcome(context.getEnvironment(), cloudPlatform);
    }
    1. 獲得@ConditionalOnCloudPlatform 所配置的CloudPlatform
    2. 呼叫getMatchOutcome進行處理,在該方法中是通過呼叫CloudPlatform的isActive來判斷.如果isActive返回true,則返回匹配,否則返回不匹配.
  3. 使用案例:

    CloudFoundryActuatorAutoConfiguration聲明瞭如下註解:

    @ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)

    表明了 只有在Cloud Foundry平臺時才載入CloudFoundryActuatorAutoConfiguration的配置.

ConditionalOnCloudPlatform

  1. @ConditionalOnExpression 程式碼如下:

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Documented
    @Conditional(OnExpressionCondition.class)
    public @interface ConditionalOnExpression {
    
        // 如果該表示式返回true則代表匹配,否則返回不匹配
        String value() default "true";
    }
  2. @ConditionalOnExpression 對應的處理類為OnExpressionCondition.程式碼如下:

    public ConditionOutcome getMatchOutcome(ConditionContext context,
            AnnotatedTypeMetadata metadata) {
        // 1. 獲得@C