SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)

微信公众号:吉姆餐厅ak
学习更多源码知识,欢迎关注。
在这里插入图片描述


SpringBoot2 | SpringBoot启动流程源码分析(一)

SpringBoot2 | SpringBoot启动流程源码分析(二)

SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)

SpringBoot2 | SpringBoot Environment源码分析(四)

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

SpringBoot2 | SpringBoot监听器源码分析 | 自定义ApplicationListener(六)

SpringBoot2 | 条件注解@ConditionalOnBean原理源码深度解析(七)

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

SpringBoot2 | SpingBoot FilterRegistrationBean 注册组件 | FilterChain 责任链源码分析(九)

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原理源码深度解析(七)

  • 29
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
SpringBoot2的启动流程是通过@SpringBootApplication注解自动化配置来实现的。该注解包含了多个注解的组合,其中包括@ComponentScan、@EnableAutoConfiguration和@Configuration等。通过这些注解,Spring Boot会自动扫描并加载配置类,并根据自动配置规则来配置应用程序。 具体而言,当应用程序启动时,Spring Boot会创建一个Spring应用程序上下文。在创建上下文的过程中,会先加载主配置类(通常是包含main方法的类),然后根据@ComponentScan注解扫描指定包下的所有组件。 接下来,Spring Boot会根据@EnableAutoConfiguration注解自动配置应用程序。这个注解会根据classpath和条件匹配的规则,加载配置类,并将它们注册到应用程序上下文中。这些配置类使用了@Configuration注解,会声明一些Bean,并根据条件来决定是否生效。 最后,Spring Boot会启动应用程序,并执行相应的事件处理器。这些事件处理器可以通过自定义ApplicationListener来实现。在应用程序运行期间,Spring Boot会触发不同的事件,并调用相应的事件处理器。 参考文献: 引用:SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析) [2] 引用:SpringBoot2 | SpringBoot监听器源码分析 | 自定义ApplicationListener(六) 引用:该系列主要还是Spring的核心源码,不过目前Springboot大行其道,所以就从Springboot开始分析。最新版本是Springboot2.0.4,Spring5,所以新特性本系列后面也会着重分析
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值