SpringBoot2 | @SpringBootApplication註解 自動化配置流程源碼分析(三)

微信公衆號:吉姆餐廳ak 學習更多源碼知識,歡迎關注。 java

在這裏插入圖片描述


SpringBoot2 | SpringBoot啓動流程源碼分析(一)spring

SpringBoot2 | SpringBoot啓動流程源碼分析(二)bash

SpringBoot2 | @SpringBootApplication註解 自動化配置流程源碼分析(三)微信

SpringBoot2 | SpringBoot Environment源碼分析(四)app

SpringBoot2 | SpringBoot自定義AutoConfiguration | SpringBoot自定義starter(五)框架

SpringBoot2 | SpringBoot監聽器源碼分析 | 自定義ApplicationListener(六)ide

SpringBoot2 | 條件註解@ConditionalOnBean原理源碼深度解析(七)工具

SpringBoot2 | Spring AOP 原理源碼深度剖析(八)源碼分析

SpringBoot2 | SpingBoot FilterRegistrationBean 註冊組件 | FilterChain 責任鏈源碼分析(九)post

SpringBoot2 | BeanDefinition 註冊核心類 ImportBeanDefinitionRegistrar (十)

SpringBoot2 | Spring 核心擴展接口 | 核心擴展方法總結(十一)


在上一篇博客中分析了springBoot啓動流程,大致的輪廓只是冰山一角。今天就來看一下springBoot的亮點功能:自動化裝配功能。


先從@SpringBootApplication開始。

在啓動流程章節中,咱們講述了SpringBoot2大體的啓動步驟,並進行了源碼詳解。可是在刷新容器這塊並未展開,refreshContext(context);簡單的一行代碼,背後卻作了太多事情。因此爲了避免喧賓奪主,本篇也儘可能選取和註解@SpringBootApplication有關的方法講解。


1)springBoot啓動類加載

首先加載springBoot啓動類注入到spring容器中bean map中,看下prepareContext方法中的load方法: load(context, sources.toArray(new Object[0])); 跟進該方法最終會執行BeanDefinitionLoaderload方法:

private int load(Object source) {
		Assert.notNull(source, "Source must not be null");
		//若是是class類型,啓用註解類型
		if (source instanceof Class<?>) {
			return load((Class<?>) source);
		}
		//若是是resource類型,啓用xml解析
		if (source instanceof Resource) {
			return load((Resource) source);
		}
		//若是是package類型,啓用掃描包,例如:@ComponentScan
		if (source instanceof Package) {
			return load((Package) source);
		}
		//若是是字符串類型,直接加載
		if (source instanceof CharSequence) {
			return load((CharSequence) source);
		}
		throw new IllegalArgumentException("Invalid source type " + source.getClass());
	}
複製代碼

繼續跟進load(Class<?> source)方法:

這裏寫圖片描述

上述方法判斷啓動類中是否包含@component註解,可咱們的啓動類並無該註解。繼續跟進會發現,AnnotationUtils判斷是否包含該註解是經過遞歸實現,註解上的註解若包含指定類型也是能夠的。 啓動類中包含@SpringBootApplication註解,進一步查找到@SpringBootConfiguration註解,而後查找到@Component註解,最後會查找到@Component註解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
}
複製代碼

在查找到@Component註解後,表面該對象爲spring bean,而後會將其信息包裝成 beanDefinitaion ,添加到容器的 beanDefinitionMap中。以下:

這裏寫圖片描述
如此一來,咱們的啓動類就被包裝成 AnnotatedGenericBeanDefinition了,後續啓動類的處理都基於該對象了。


2)自動裝配的入口:

從刷新容器開始:
public void refresh() throws BeansException, IllegalStateException {
		//...
		invokeBeanFactoryPostProcessors(beanFactory);
		//...
	}
複製代碼

上述省去了不相關的代碼,繼續跟進invokeBeanFactoryPostProcessors方法:

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		//開始執行beanFactoryPostProcessor對應實現類
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

		// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
		// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
		if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}
	}
複製代碼

首先咱們要知道beanFactoryPostProcessor接口是spring的擴展接口,從名字也能夠看出,是 beanFactory的擴展接口。在刷新容器以前,該接口可用來修改bean元數據信息。具體實現方式,咱們繼續跟着上述執行邏輯便知。 繼續跟進上面invokeBeanFactoryPostProcessors方法,第一行很關鍵:

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
複製代碼

一個比較核心的代理類出現了,AbstractApplicationContext委託執行post processors任務的工具類而在項目啓動時會委託什麼任務呢?

或許你還記得第一篇博客中介紹的SpringApplication類中applyInitializers(context);方法吧,它會將三個默認的內部類加入到 spring 容器DefaultListableBeanFactory中,以下:

//設置配置警告
ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor
SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor
ConfigFileApplicationListener$PropertySourceOrderingPostProcessor
複製代碼

來看一下具體任務執行細節,跟進invokeBeanFactoryPostProcessors方法:

if (beanFactory instanceof BeanDefinitionRegistry) {
			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
			List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<>();
			List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList<>();

		
			//這裏開始遍歷上面三個內部類,若是屬於BeanDefinitionRegistryPostProcessor 子類,
			//加入到bean註冊的集合,不然加入到 regularPostProcessors中,從名字能夠看出是有規律集合。
			for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
				if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
					BeanDefinitionRegistryPostProcessor registryProcessor =
							(BeanDefinitionRegistryPostProcessor) postProcessor;
					registryProcessor.postProcessBeanDefinitionRegistry(registry);
					registryProcessors.add(registryProcessor);
				}
				else {
					regularPostProcessors.add(postProcessor);
				}
			}

			// Do not initialize FactoryBeans here: We need to leave all regular beans
			// uninitialized to let the bean factory post-processors apply to them!
			// Separate between BeanDefinitionRegistryPostProcessors that implement
			// PriorityOrdered, Ordered, and the rest.
			List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

			// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
			String[] postProcessorNames =
					beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
					
			//首先執行類型爲PriorityOrdered的BeanDefinitionRegistryPostProcessor
			//PriorityOrdered類型代表爲優先執行
			for (String ppName : postProcessorNames) {
				if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
					//獲取對應的bean
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					//用來存儲已經執行過的`BeanDefinitionRegistryPostProcessor`				
					processedBeans.add(ppName);
				}
			}
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			currentRegistryProcessors.clear();

			// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
			postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			//其次執行類型爲Ordered的BeanDefinitionRegistryPostProcessor
			//Ordered代表按順序執行
			for (String ppName : postProcessorNames) {
				if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
				}
			}
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			currentRegistryProcessors.clear();

			// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
			boolean reiterate = true;
			//循環中執行類型不爲PriorityOrdered,Ordered類型的BeanDefinitionRegistryPostProcessor
			while (reiterate) {
				reiterate = false;
				postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
				for (String ppName : postProcessorNames) {
					if (!processedBeans.contains(ppName)) {
						currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
						processedBeans.add(ppName);
						reiterate = true;
					}
				}
				sortPostProcessors(currentRegistryProcessors, beanFactory);
				registryProcessors.addAll(currentRegistryProcessors);
				invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
				currentRegistryProcessors.clear();
			}

			// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
			//執行父類方法,優先執行註冊處理類
			invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
			//執行有規則處理類
			invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
		}
複製代碼

來分析一下核心代碼:

String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
複製代碼

這行代碼經過類型BeanDefinitionRegistryPostProcessor獲取的處理類名稱爲: "org.springframework.context.annotation.internalConfigurationAnnotationProcessor" 而在源碼中卻搜不到internalConfigurationAnnotationProcessor類,爲何呢?最初看這塊代碼確實迷惑了半天。 在第一篇博客中,當啓動springBoot,建立springBoot容器上下文AnnotationConfigEmbeddedWebApplicationContext時,會裝配幾個默認bean:

public AnnotationConfigEmbeddedWebApplicationContext() {
		//在這裏裝配
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
複製代碼

繼續跟進會執行registerAnnotationConfigProcessors方法:

public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =
			"org.springframework.context.annotation.internalConfigurationAnnotationProcessor";

	//將 internalConfigurationAnnotationProcessor 對應的類包裝成 RootBeanDefinition 加載到容器
	if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
複製代碼

到這裏,答案清晰浮現。internalConfigurationAnnotationProcessor爲bean名稱,容器中真正的類則是ConfigurationClassPostProcessor

繼續後面流程,獲取ConfigurationClassPostProcessor後,開始執行BeanDefinitionRegistryPostProcessor:

//開始執行裝配邏輯
invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);
複製代碼

3)開始執行自動配置邏輯(啓動類指定的配置,非默認配置):

這裏寫圖片描述

如上如:首先得到ConfigurationClassParser,這個是全部配置類的解析類,比較核心。全部的解析邏輯在parser.parse(candidates);中,咱們詳細的來看一下:

public void parse(Set<BeanDefinitionHolder> configCandidates) {
		this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();

		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				//是不是註解類
				if (bd instanceof AnnotatedBeanDefinition) {
					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());
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Exception ex) {
				throw new BeanDefinitionStoreException(
						"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
			}
		}

		//執行配置類
		processDeferredImportSelectors();
	}
複製代碼

繼續跟進parse方法:

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
		//...省略不核心代碼
		// Recursively process the configuration class and its superclass hierarchy.
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			//循環處理bean,若是有父類,則處理父類。直至結束。
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);

		this.configurationClasses.put(configClass, configClass);
	}
複製代碼

繼續跟進doProcessConfigurationClass方法,該方法能夠說是 spring 框架支持註解配置的核心邏輯了,來看看:

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
        //處理內部類邏輯,因爲傳來的參數是咱們的啓動類,不含內部類,因此跳過。
        processMemberClasses(configClass, sourceClass);

        // Process any @PropertySource annotations
        //針對屬性配置的解析
        for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
                sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) {
            if (this.environment instanceof ConfigurableEnvironment) {
                processPropertySource(propertySource);
            }
            else {
                logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                        "]. Reason: Environment must implement ConfigurableEnvironment");
            }
        }

        //這裏是根據啓動類 @ComponentScan 註解來掃描項目中的bean
        AnnotationAttributes componentScan = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ComponentScan.class);
        if (componentScan != null && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
            // The config class is annotated with @ComponentScan -> perform the scan immediately
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            // Check the set of scanned definitions for any further config classes and parse recursively if necessary

            //遍歷咱們項目中的bean,若是是註解定義的bean,則進一步解析
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                //判斷是不是註解bean
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
                    //這裏是關鍵,遞歸解析。全部的bean,若是有註解,會進一步解析註解中包含的bean
                    parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
                }
            }
        }

        // Process any @Import annotations
        //這裏又是一個遞歸解析,獲取導入的配置類。不少狀況下,導入的配置類中會一樣包含導入類註解。
        processImports(configClass, sourceClass, getImports(sourceClass), true);

        // Process any @ImportResource annotations
        //解析導入的 xml 配置類
        if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
            AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
            String[] resources = importResource.getAliasedStringArray("locations", ImportResource.class, sourceClass);
            Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
            for (String resource : resources) {
                String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
                configClass.addImportedResource(resolvedResource, readerClass);
            }
        }

        // Process individual @Bean methods
        Set<MethodMetadata> beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());
        for (MethodMetadata methodMetadata : beanMethods) {
            configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
        }

        // 獲取接口中的默認方法,1.8以上的處理邏輯
        for (SourceClass ifc : sourceClass.getInterfaces()) {
            beanMethods = ifc.getMetadata().getAnnotatedMethods(Bean.class.getName());
            for (MethodMetadata methodMetadata : beanMethods) {
                if (!methodMetadata.isAbstract()) {
                    // A default method or other concrete method on a Java 8+ interface...
                    configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
                }
            }
        }

        // Process superclass, if any
        //若是該類有父類,則繼續返回。上層方法判斷不爲空,則繼續遞歸執行。
        if (sourceClass.getMetadata().hasSuperClass()) {
            String superclass = sourceClass.getMetadata().getSuperClassName();
            if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
                this.knownSuperclasses.put(superclass, configClass);
                // Superclass found, return its annotation metadata and recurse
                return sourceClass.getSuperClass();
            }
        }

        // No superclass -> processing is complete
        //遞歸實現,superclass爲空,則結束遞歸中的循環
        return null;
    }
複製代碼

來看一下獲取導入配置類的邏輯:

processImports(configClass, sourceClass, getImports(sourceClass), true);
複製代碼

跟進getImports方法:

這裏寫圖片描述
能夠看到我自定義的bean以導入的方式被加載進去了。另外 processImports方法執行邏輯和上述 parse方法相似,一樣採用遞歸處理,這裏就不作展開了。


4)開始執行 SpringBoot 默認配置邏輯

繼續回到ConfigurationClassParser中的parse方法,回到該方法的最後一步:

public void parse(Set<BeanDefinitionHolder> configCandidates) {
		//...
		//開始執行默認配置
		processDeferredImportSelectors();
	}
複製代碼

繼續跟進該方法processDeferredImportSelectors

private void processDeferredImportSelectors() {
		List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
		this.deferredImportSelectors = null;
		Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);

		for (DeferredImportSelectorHolder deferredImport : deferredImports) {
			ConfigurationClass configClass = deferredImport.getConfigurationClass();
			try {
				//獲取配置類
				String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
				//再次遞歸解析配置類
				processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
						configClass.getMetadata().getClassName() + "]", ex);
			}
		}
	}
複製代碼

getImportSelector()方法獲取的 selector對象爲EnableAutoConfigurationImportSelector,繼續跟進該對象的selectImports方法:

@Override
	public String[] selectImports(AnnotationMetadata metadata) {
		try {
			AnnotationAttributes attributes = getAttributes(metadata);
			//獲取默認配置類
			List<String> configurations = getCandidateConfigurations(metadata,
					attributes);
			configurations = removeDuplicates(configurations);
			Set<String> exclusions = getExclusions(metadata, attributes);
			configurations.removeAll(exclusions);
			configurations = sort(configurations);
			recordWithConditionEvaluationReport(configurations, exclusions);
			return configurations.toArray(new String[configurations.size()]);
		}
		catch (IOException ex) {
			throw new IllegalStateException(ex);
		}
	}
複製代碼

這裏的處理方式,前面的博客中已經詳細介紹過了,經過class類型來獲取spring.factories中的指定類,class類型爲:EnableAutoConfiguration

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}
複製代碼

springBoot爲咱們提供的全部配置類以下,大概100多個:

這裏寫圖片描述

在獲取到springBoot提供的配置後,再次調用processImports方法進行遞歸解析,根據咱們自定義的配置文件,進行選擇性配置。

這麼多的配置類,不可能所有進行加載,項目也用不了這麼多。選擇的規則是什麼呢?

後續會有完整博文詳細介紹。


springBoot自動化裝配流程就先介紹到這裏。


SpringBoot2 | SpringBoot啓動流程源碼分析(一)

SpringBoot2 | SpringBoot啓動流程源碼分析(二)

SpringBoot2 | @SpringBootApplication註解 自動化配置流程源碼分析(三)

SpringBoot2 | SpringBoot Environment源碼分析(四)

SpringBoot2 | SpringBoot自定義AutoConfiguration | SpringBoot自定義starter(五)

SpringBoot2 | SpringBoot監聽器源碼分析 | 自定義ApplicationListener(六)

SpringBoot2 | 條件註解@ConditionalOnBean原理源碼深度解析(七)

相關文章
相關標籤/搜索