springboot情操陶冶-@Conditional和@AutoConfigureAfter注解解析


承接前文springboot情操陶冶-@Configuration注解解析,本文將在前文的基礎上闡述@AutoConfigureAfter@Conditional注解的作用與解析

1.@Conditional

根據單詞來理解,其就是條件的意思。在分析之前我們可以看下其內部源碼

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

	/**
	 * All {@link Condition}s that must {@linkplain Condition#matches match}
	 * in order for the component to be registered.
	 */
	Class<? extends Condition>[] value();

}

其作用於類、方法上,且指定的value值必須是org.springframework.context.annotation.Condition的實現類,供條件判斷。
以此為基礎而擴展的注解還有@ConditionalBean@ConditionalOnWebApplication@ConditionalOnClass@ConditionalOnMissingBean等等。

@Conditional注解被解析入口

那么我們肯定想知道,其中的注解是如何被解析的呢。其實在前文中的ConfigurationClassParser類中,在執行真正的doProcessConfigurationClass()方法前,會執行如下的代碼

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
		// 條件判斷,滿足則直接返回,不進行后續的解析
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}

		....

		// Recursively process the configuration class and its superclass hierarchy.
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);

		this.configurationClasses.put(configClass, configClass);
	}

也就是會執行上述的ConditionEvaluator#shouldSkip()方法,只有條件不滿足后才會繼續往下執行真正的@Configuration注解解析。

ConditionEvaluator#shouldSkip()

廢話不多說,直接上源碼

	// metadata為被注解的類元素,返回值為true表明條件滿足應該被忽略
	public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
		// 1.判斷類是否含有@Conditional注解,否則直接返回
		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);
		}

		// 2.獲取類上所有含有@Conditional注解的value集合(其會遞歸找尋注解的注解)
		List<Condition> conditions = new ArrayList<>();
		for (String[] conditionClasses : getConditionClasses(metadata)) {
			for (String conditionClass : conditionClasses) {
				Condition condition = getCondition(conditionClass, this.context.getClassLoader());
				conditions.add(condition);
			}
		}
		
		// 3.根據Order來進行排序
		AnnotationAwareOrderComparator.sort(conditions);
		
		// 4.對集合內的condition統一調用matches()方法,一旦遇到條件判斷不滿足的則返回true對此注解類元素進行忽略
		for (Condition condition : conditions) {
			ConfigurationPhase requiredPhase = null;
			if (condition instanceof ConfigurationCondition) {
				requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
			}
			if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
				return true;
			}
		}

		return false;
	}

具體的代碼解釋已經按照注釋給出了,其實也很簡單,讀者稍微閱讀就能明白了。另外額外的注解比如@ConditionalOnMissingBean等讀者可自行去閱讀代碼分析,筆者此處就不展開了

2.@AutoConfigureAfter

@AutoConfigureBefore類同,代表的含義就是自動注入在什么類加載前或者之后。先來看下其內部源碼

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Documented
public @interface AutoConfigureAfter {

	/**
	 * The auto-configure classes that should have already been applied.
	 * @return the classes
	 */
	Class<?>[] value() default {};

	/**
	 * The names of the auto-configure classes that should have already been applied.
	 * @return the class names
	 * @since 1.2.2
	 */
	String[] name() default {};

}

只作用於類上,內部屬性name表明beanDefinition的類名;內部屬性value表明beanDefinition的類。
那么其是如何被解析的呢,也是基於前文的ConfigurationClassParser#parse()方法,具體如下

	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		this.deferredImportSelectors = new LinkedList<>();
		// 解析@Configuration注解
		....
		// 解析DeferredImportSelector接口類,表面上也就是延遲解析的意思
		processDeferredImportSelectors();
	}

筆者此處只關注processDeferredImportSelectors()方法,通過此方法便可察覺到@AutoConfigureAfter等注解的蛛絲馬跡

ConfigurationClassParser#processDeferredImportSelectors()

直接閱讀源碼

	private void processDeferredImportSelectors() {
		// 1.通過processImport()方法得到DeferredImportSelector接口集合,無則直接返回
		List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
		this.deferredImportSelectors = null;
		if (deferredImports == null) {
			return;
		}
		
		// 2.排序
		deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
		// 3.遍歷DeferredImportSelector接口集合,獲取Group集合類,默認為DefaultDeferredImportSelectorGroup
		Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
		Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
		for (DeferredImportSelectorHolder deferredImport : deferredImports) {
			// notice this........
			Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
			DeferredImportSelectorGrouping grouping = groupings.computeIfAbsent(
					(group != null ? group : deferredImport),
					key -> new DeferredImportSelectorGrouping(createGroup(group)));
			grouping.add(deferredImport);
			configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
					deferredImport.getConfigurationClass());
		}
		// 4. 遍歷Group集合,作用也是調用processImport()方法用於解析@Import
		for (DeferredImportSelectorGrouping grouping : groupings.values()) {
			grouping.getImports().forEach(entry -> {
				ConfigurationClass configurationClass = configurationClasses.get(entry.getMetadata());
				try {
					processImports(configurationClass, asSourceClass(configurationClass),
							asSourceClasses(entry.getImportClassName()), false);
				}
				catch (BeanDefinitionStoreException ex) {
					throw ex;
				}
				catch (Throwable ex) {
					throw new BeanDefinitionStoreException(
							"Failed to process import candidates for configuration class [" +
							configurationClass.getMetadata().getClassName() + "]", ex);
				}
			});
		}
	}

筆者和讀者此處只需要關注deferredImport.getImportSelector().getImportGroup()這個方法即可,此處以AutoConfigurationImportSelector.class為例

AutoConfigurationImportSelector

首先看下其getImportGroup()方法

public Class<? extends Group> getImportGroup() {
		return AutoConfigurationGroup.class;
	}

再觀察下AutoConfigurationGroup此類的selectImports()方法

		public Iterable<Entry> selectImports() {
			return sortAutoConfigurations().stream()
					.map((importClassName) -> new Entry(this.entries.get(importClassName),
							importClassName))
					.collect(Collectors.toList());
		}

關鍵點來了,就在sortAutoConfigurations()方法,其會通過AutoConfigurationSorter類來對導入的class類進行排序,至於如何排序我們繼續往下看

AutoConfigurationSorter

排序方法getInPriorityOrder(),我們看下源碼

	public List<String> getInPriorityOrder(Collection<String> classNames) {
		AutoConfigurationClasses classes = new AutoConfigurationClasses(
				this.metadataReaderFactory, this.autoConfigurationMetadata, classNames);
		List<String> orderedClassNames = new ArrayList<>(classNames);
		// Initially sort alphabetically.首先根據ASCII來進行排序
		Collections.sort(orderedClassNames);
		// Then sort by order,再根據Order來進行排序
		orderedClassNames.sort((o1, o2) -> {
			int i1 = classes.get(o1).getOrder();
			int i2 = classes.get(o2).getOrder();
			return Integer.compare(i1, i2);
		});
		// Then respect @AutoConfigureBefore @AutoConfigureAfter
		orderedClassNames = sortByAnnotation(classes, orderedClassNames);
		return orderedClassNames;
	}

可以得出,最關鍵的排序來自sortByAnnotation()方法,具體就不看了,無非是根據before/after,來對importClassName進行排序得出一個有序的集合。

1.最后再回到ConfigurationClassParser#processDeferredImportSelectors()方法的最后一段,其會對上述的有序的集合遍歷操作processImports()方法,如果對應的class類不存在則會報錯,也就滿足了AutoConfigureBefore/AutoConfigureAfter的含義。

2.上述的@AutoConfigureAfter注解解析只作用於META-INF\spring.factories文件中EnableAutoConfiguration屬性對應的class類集合。
(v2.0版本以下支持用戶使用該注解直接應用自定義類;v2.0版本以上,如果用戶也使用了該注解,也需要在META-INF\spring.factories配置相應的EnableAutoConfiguration屬性)

小結

針對@Conditional@AutoConfigureAfter的具體解析可見上文,本文也是對前文的補充。希望讀者在閱讀此文的同時務必閱讀前文方可理解上述的代碼含義。同時因為這兩個注解具有條件性,所以springboot多用此兩注解來相互搭配構建不同條件的依賴部署,對去配置化起到了很大的作用。以WebMvcAutoConfiguration類作為結尾

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM