微信公衆號:吉姆餐廳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
有關的方法講解。
首先加載springBoot啓動類注入到spring容器中bean map中,看下prepareContext方法中的load方法: load(context, sources.toArray(new Object[0]));
跟進該方法最終會執行BeanDefinitionLoader
的load
方法:
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
了,後續啓動類的處理都基於該對象了。
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);
複製代碼
如上如:首先得到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
方法:
processImports
方法執行邏輯和上述
parse
方法相似,一樣採用遞歸處理,這裏就不作展開了。
繼續回到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(五)