spring boot 原始碼解析[email protected]
前言
之前在分析spring boot 原始碼時匯出可見@ConditionalOnBean 之類的註解,那麼它到底是如何使用的以及其工作流程如何,我們這裡就圍繞以下幾點來分析:
- @Conditional系列與Condition的關係
- @Conditional與Condition的原始碼分析
- 總結
@Conditional系列與Condition的關係
spring boot 中有關Condition的程式碼在org.springframework.boot.autoconfigure.condition中,如圖:
可以看到類還是很多的,但是基本上,都是一個註解對應一個condition實現類.拿其中@ConditionalOnBean,@ConditionalOnClass 為例,其類圖如下:
@Conditional與Condition的原始碼分析
從以上的類圖可以知道,所有的contidon類都是通過繼承SpringBootCondition的方式實現的(實現了Condition介面).Condition介面定義如下:
public interface Condition { boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
matches方法判斷其條件是否成立,如果不成立,則會阻止該bean的註冊.
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 級別
4步:
得到類名或者方法名(條件註解可以作用的類或者方法上).程式碼如下:
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的例項,則返回類名,否則返回全類名#方法名
- 抽象方法,具體子類實現。ConditionOutcome記錄了匹配結果boolean和log資訊
列印日誌,Trace 級別.程式碼如下:
protected final void logOutcome(String classOrMethodName, ConditionOutcome outcome) { if (this.logger.isTraceEnabled()) { this.logger.trace(getLogMessage(classOrMethodName, outcome)); } }
記錄結果.程式碼如下:
private void recordEvaluation(ConditionContext context, String classOrMethodName, ConditionOutcome outcome) { if (context.getBeanFactory() != null) { ConditionEvaluationReport.get(context.getBeanFactory()) .recordConditionEvaluation(classOrMethodName, this, outcome); } }
此外,SpringBootCondition 還聲明瞭2個比較有用的方法,供子類使用:
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; }
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即可.
接下來,我們就依次分析org.springframework.boot.autoconfigure.condition中的原始碼.
ConditionalOnBean
@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 }
@Conditional對應的處理類是OnBeanCondition,其除了繼承SpringBootCondition外,還實現了ConfigurationCondition介面.類圖如下:
可以看到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); }
構造一個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); }
- 對annotationType進行賦值
- 獲得metadata所有的屬性所對應的值,封裝為MultiValueMap,key–>屬性名,value–>所對應的值,class 轉換為String
呼叫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); } } } }
如果types沒有設定並且names也沒有設定,則如果該metadata是MethodMetadata的例項並且該metadata被@Bean註解則將該方法的返回值型別作為types
- 檢驗,如果types,names,annotations 都為空,則丟擲IllegalStateException異常
呼叫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; }
- 如果搜尋策略為PARENTS或者ANCESTORS,則beanFactory為當前容器的父容器中獲取.否則beanFactory從當前容器獲取
- 如果beanFactory等於空,則返回空集合.該情況是對於父容器才會發生的
- 從beanFactory中獲得給定型別的beanIds,如果需要從父容器中搜索,則該方法會合並父容器的介面
- 從beanNames刪除給定忽略型別的bean,如果需要從父容器中搜索,則該方法會將父容器中包含給定type的bean刪除
- 遍歷給定的Annotations,依次從beanFactory中獲取聲明瞭該Annotation的bean,如果需要從父容器中搜索,則也會將父容器包含的新增進去
- 遍歷給定的ids,從當前容器和父容器中(如果需要)查詢,如果包含的話,則加入到beanNames
注意,如果同時指定了Types,Names 其返回的結果不是and,而是or
- 如果沒有匹配的bean,返回一個沒有匹配成功的ConditionalOutcome.最終返回false.
- 否則,返回匹配.最終返回true.
使用案例:
在CacheStatisticsAutoConfiguration類中聲明瞭如下註解:
@ConditionalOnBean(CacheManager.class)
標識當 CacheManager型別的bean存在時才對CacheStatisticsAutoConfiguration進行處理.
ConditionalOnSingleCandidate
@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 屬性不能同時出現,只能使用一個
所對應的處理類為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);
例項化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() + ")"); }
獲得給定type的beanNames
- 如果不存在,則返回不匹配
- 如果給定型別的bean存在多個但是指定為Primary的不存在,則返回不匹配
- 返回匹配
使用案例:
在DataSourceTransactionManagerConfiguration 聲明瞭如下註解:
@Configuration @ConditionalOnSingleCandidate(DataSource.class) static class DataSourceTransactionManagerConfiguration
標識:當DataSource型別的bean存在並且指定為Primary的DataSource存在時,載入DataSourceTransactionManagerConfiguration的配置
ConditionalOnMissingBean
@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; }
@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);
- 例項化BeanSearchSpec
- 獲得給定條件的beanNames
- 如果不為空,返回不匹配,否則返回匹配
使用案例:
在DataSourceAutoConfiguration中聲明瞭如下方法:
@Bean @ConditionalOnMissingBean public DataSourceInitializer dataSourceInitializer(DataSourceProperties properties, ApplicationContext applicationContext) { return new DataSourceInitializer(properties, applicationContext); }
表明當beanFactory中不存在DataSourceInitializer型別的bean時,才進行註冊
ConditionalOnClass與ConditionalOnMissingClass
@ConditionalOnClass與@ConditionalOnMissingClass 對應的處理類都是OnClassCondition.這裡就一起分析了
@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 {}; }
OnClassCondition類圖如下:
其中AutoConfigurationImportFilter的作用是將在spring.factories中定義的auto-configuration 的類名進行過濾.該介面的目標是快速去除不需要的類在對其配置解析前.一個AutoConfigurationImportFilter介面的實現可能需要實現EnvironmentAware,BeanFactoryAware,BeanClassLoaderAware,ResourceLoaderAware介面中的任意個.這些介面會在呼叫match方法前進行注入.該方法的呼叫鏈如下:
在AutoConfigurationImportSelector中會載入spring.factories中配置的org.springframework.boot.autoconfigure.AutoConfigurationImportFilter,其配置的剛好就是OnClassCondition.因此該類會在此刻被例項化,進行處理.程式碼如下:
# Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnClassCondition
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; }
- 獲得ConditionEvaluationReport.該ConditionEvaluationReport只會在beanFactory中例項化1個.
- 呼叫getOutcomes 獲得ConditionOutcome[].
- 初始化match,該陣列只儲存符合要求的
依次遍歷outcomes
- 對match中的陣列進行賦值,當outcomes等於null 或者 對應下標的ConditionOutcome匹配時為true.其他情況,返回false.一般outcomes都是null.
- 如果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個:
StandardOutcomesResolver.
欄位如下:
// 在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;
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; }
- 例項化ConditionOutcome[],大小為end - start
- 遍歷給定的autoConfigurationClasses,依次從autoConfigurationMetadata中獲得通過autoConfigurationClass+”.“+ ConditionalOnClass 所對應的配置(即autoConfigurationClass要生效所需要的類),如果存在的話,則進入第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.(這裡比較繞,仔細想一下就明白了)
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執行完.
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); }
- 得到@ConditionalOnClass註解的屬性,注意: value和name的屬性可以不一樣,是and的關係
- 如果onClasses不為空的話,則呼叫getMatches進行處理,getMatches方法我們之前已經分析過了,如果有給定的類在當前的類路徑上不存在的話,則返回不匹配.否則進行記錄
得到@ConditionalOnMissingClass註解的屬性.如果不為空的話,則呼叫getMatches進行處理,getMatches方法我們之前已經分析過了,如果有給定的類在當前的類路徑上存在的話,則返回不匹配.否則進行記錄.這裡呼叫的是PRESENT#matches方法.程式碼如下:
@Override public boolean matches(String className, ClassLoader classLoader) { return isPresent(className, classLoader); }
- 最終,返回匹配.
使用案例:
AopAutoConfiguration聲明瞭如下註解:
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class })
表明當在當前類路徑存在EnableAspectJAutoProxy.class, Aspect.class, Advice.class時才對AopAutoConfiguration進行解析
Thymeleaf2Configuration 聲明瞭如下註解:
@ConditionalOnMissingClass("org.thymeleaf.templatemode.TemplateMode")
表明當在當前類路徑不存在org.thymeleaf.templatemode.TemplateMode時才對Thymeleaf2Configuration進行解析
ConditionalOnCloudPlatform
@ConditionalOnCloudPlatform 程式碼如下:
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnCloudPlatformCondition.class) public @interface ConditionalOnCloudPlatform { // 給定的CloudPlatform必須是啟用狀態時才返回true CloudPlatform value(); }
CloudPlatform是一個列舉,其聲明瞭2個方法以供列舉使用:
isUsingForwardHeaders–>表明當前的平臺是否使用X-Forwarded-For這個頭部來進行負載均衡.預設為true.程式碼如下;
public boolean isUsingForwardHeaders() { return true; }
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; }
聲明瞭一個isActive抽象方法–>列舉實現,如果返回true,則表明該spirng boot 應用執行在列舉所對應的雲平臺中.CloudPlatform有2個列舉型別,其實現分別如下:
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"); } }
HEROKU–> Heroku 平臺.通過判斷當前給定環境變數是否存在DYNO對應的屬性.程式碼如下:
HEROKU { @Override public boolean isActive(Environment environment) { return environment.containsProperty("DYNO"); } }
@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); }
- 獲得@ConditionalOnCloudPlatform 所配置的CloudPlatform
- 呼叫getMatchOutcome進行處理,在該方法中是通過呼叫CloudPlatform的isActive來判斷.如果isActive返回true,則返回匹配,否則返回不匹配.
使用案例:
CloudFoundryActuatorAutoConfiguration聲明瞭如下註解:
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
表明了 只有在Cloud Foundry平臺時才載入CloudFoundryActuatorAutoConfiguration的配置.
ConditionalOnCloudPlatform
@ConditionalOnExpression 程式碼如下:
@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(OnExpressionCondition.class) public @interface ConditionalOnExpression { // 如果該表示式返回true則代表匹配,否則返回不匹配 String value() default "true"; }
@ConditionalOnExpression 對應的處理類為OnExpressionCondition.程式碼如下:
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { // 1. 獲得@C