淺析SpringBoot自動裝配的實現

背景

眾所周知,如下即可啟動一個最簡單的Spring應用。查看@SpringBootApplication註解的源碼,發現這個註解上有一個重要的註解@EnableAutoConfiguration,而這個註解就是SpringBoot實現自動裝配的基礎

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

@EnableAutoConfiguration

EnableAutoConfiguration註解上通過@Import引入瞭兩個類,org.springframework.boot.autoconfigure.AutoConfigurationImportSelectororg.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar。通過@Import標註的類,會在解析@Import所在的配置類時,將標註類引入容器解析,並進行註冊。

有眾多的組件都是通過在配置類上加@EnableAutoConfiguration註解將組件引入的

  • ImportBeanDefinitionRegistrar實現瞭org.springframework.context.annotation.ImportBeanDefinitionRegistrarorg.springframework.boot.context.annotation.DeterminableImports
  • AutoConfigurationImportSelector實現瞭org.springframework.context.annotation.DeferredImportSelector
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ....
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
    ...
}

解析

起始

通過BeanFactoryPostProcessor對需要註冊的Bean進行解析。即org.springframework.context.support.AbstractApplicationContext#refresh,在AbstractApplicationContext#invokeBeanFactoryPostProcessors方法調用時,就開始瞭對服務配置bean的解析,為對象的生成做準備

 @Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			...

			try {
				...
                    
				invokeBeanFactoryPostProcessors(beanFactory);
			}
			catch (BeansException ex) {
			finally {
		}
	}

具體解析

調用org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors,通過獲取到的BeanFactoryPostProcessor實現類對各種配置類進行解析,具體的BeanFactoryPostProcessor解析後面我們在具體分析。

這裡有一個很重要的類org.springframework.context.annotation.ConfigurationClassPostProcessor,首先會調用postProcessBeanDefinitionRegistry方法

// ConfigurationClassPostProcessor類部門源碼

	/**
	 * Derive further bean definitions from the configuration classes in the registry.
	 */
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		int registryId = System.identityHashCode(registry);
		if (this.registriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
		}
		if (this.factoriesPostProcessed.contains(registryId)) {
					"postProcessBeanFactory already called on this post-processor against " + registry);
		this.registriesPostProcessed.add(registryId);
	
                // 處理配置類
		processConfigBeanDefinitions(registry);
	}
	 * Build and validate a configuration model based on the registry of
	 * {@link Configuration} classes.
	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		...
		// Parse each @Configuration class
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);
                // configCandidates為待解析的Configuration類,如配置瞭@SpringBootApplication的類
		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
                        // 開始解析
			parser.parse(candidates);
			parser.validate();
			...
		while (!candidates.isEmpty());

通過源碼可知,具體的解析操作是在org.springframework.context.annotation.ConfigurationClassParser類中

public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				if (bd instanceof AnnotatedBeanDefinition) {
                                        // 將配置類進行解析。以當前配置類為原配置類,解析@PropertySource、@ComponentScan、@Import、@ImportResource、					 
                                        // @Bean等標註的類或方法,生成對應的
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				else {
					parse(bd.getBeanClassName(), holder.getBeanName());
			}
			...
		}

   		// 解析通過@Import引入的配置類,自動配置類的解析也在於此
		this.deferredImportSelectorHandler.process();
	}
	public void processGroupImports() {
			for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
				Predicate<String> exclusionFilter = grouping.getCandidateFilter();
                                // grouping.getImports()方法獲取到瞭所有配置的可用自動配置類,然後遍歷,以配置類原點又開始一輪解析。自動裝配就是在此處
				grouping.getImports().forEach(entry -> {
					ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
					try {
                                                // import的解析
						processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
								Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
								exclusionFilter, false);
					}
					catch (BeanDefinitionStoreException ex) {
						throw ex;
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to process import candidates for configuration class [" +
										configurationClass.getMetadata().getClassName() + "]", ex);
				});

通過DeferredImportSelectorGrouping.getImports()方法解析。在org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry方法中開始瞭autoConfiguration的解析。

/**
	 * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
	 * of the importing {@link Configuration @Configuration} class.
	 * @param annotationMetadata the annotation metadata of the configuration class
	 * @return the auto-configurations that should be imported
	 */
	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
                // 解析@EnableAutoConfiguration註解中的屬性exclude、excludeName
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
                // 使用SpringFactoriesLoader獲取META-INF/spring.properties中配置的EnableAutoConfiguration實現類,獲取所有配置的自動裝配類
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
                // 去重
		configurations = removeDuplicates(configurations);
                // 獲取需要排除的自動裝配類
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
                //getConfigurationClassFilter()方法就是獲取spring.factories中配置的AutoConfigurationImportFilter實現類。然後調用filter		//法對自動裝配類進行有效性校驗
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

再繼續看org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.ConfigurationClassFilter#filter

List<String> filter(List<String> configurations) {
			long startTime = System.nanoTime();
			String[] candidates = StringUtils.toStringArray(configurations);
			boolean skipped = false;
			for (AutoConfigurationImportFilter filter : this.filters) {
                                // autoConfigurationMetadata為通過META-INF/spring-autoconfigure-metadata.properties配置文件的內容
                                // 使用filter及autoConfigurationMetadata對candidates進行校驗
				boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
				for (int i = 0; i < match.length; i++) {
					if (!match[i]) {
						candidates[i] = null;
						skipped = true;
					}
				}
			}
			if (!skipped) {
				return configurations;
			}
        	...
			return result;
		}

再繼續看match方法,org.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition#match

@Override
	public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
		ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
                // 抽象方法,不同的filter進行不同的處理。這裡會獲取每一個自動裝配類的條件判斷情況
		ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
		boolean[] match = new boolean[outcomes.length];
		for (int i = 0; i < outcomes.length; i++) {
			match[i] = (outcomes[i] == null || outcomes[i].isMatch());
			if (!match[i] && outcomes[i] != null) {
				logOutcome(autoConfigurationClasses[i], outcomes[i]);
				if (report != null) {
					report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
				}
			}
		}
		return match;
	}

通過match方法,經過多種filter的過濾,返回的就是每一個自動配置類是否可用

結論

  • SpringBoot項目有一個子項目org.springframework.boot:spring-boot-autoconfigure:xx,這個子項目主要就是做自動裝配的。SpringBoot提前配置瞭眾多已經實現自動配置功能的配置類(org.springframework.boot.autoconfigure.EnableAutoConfiguration接口的實現類)。當容器啟動的時候,通過 SpringFactoriesLoader將配置類加載進容器中
  • 啟動中,容器通過BeanFactoryPostProcessor接口解析、修改對象的定義。有一個很重要的配置解析實現類org.springframework.context.annotation.ConfigurationClassPostProcessor,用來解析項目中標註@Configuration 的類
  • 在進行配置類解析時(即解析配置瞭@SpringBootApplication註解的類),需要經過解析類的 @PropertySource@ComponentScan@Import@ImportResource@Bean、接口默認實現、父類等(org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass)。對於自動裝配來說,最重要的就是解析@Import
  • 通過@Import引入瞭org.springframework.boot.autoconfigure.AutoConfigurationImportSelector,在進行解析@Import引入的配置類時,org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry獲取到所有配置的自動裝配類(通過META-INF/spring.factories文件配置EnableAutoConfiguration實現類),通過org.springframework.context.annotation.Condition定義過濾器,判斷自動裝配置是否需要自動裝配。默認的過濾器有OnClassConditionOnWebApplicationConditionOnBeanCondition,對應常見的condition註解ConditionalOnClassConditionalOnBean@ConditionalOnWebApplication
  • 通過過濾判斷,將需要自動配置的類進行configuration解析,從而將需要配置的類轉換成對應的BeanDefinition進行註冊

備註

  • SpringBoot將自動裝配類及過濾條件通過配置文件的形式放在瞭META-INF目錄下,META-INF/spring.factoriesMETA-INF/spring-autoconfigure-metadata.properties
  • BeanFactoryPostProcessor進行調用時,有兩種處理。首先是通過BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry解析更多的BeanDefinition,在這裡就包含瞭所有標註類的掃描解析,自動裝配類的解析,自動裝配類引入類的解析。在進行BeanFactoryPostProcessor#postProcessBeanFactory調用,進行CGLIB-enhanced配置類。這裡最重要的一個類就是org.springframework.context.annotation.ConfigurationClassPostProcessor,以下為此類的繼承關系

到此這篇關於SpringBoot是如何做到自動裝配的的文章就介紹到這瞭,更多相關SpringBoot自動裝配內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: