SpringBoot 启动之完成Spring上下文准备

注:Spring Boot版本:2.3.7

SpringApplication准备阶段会依次准备一些核心对象:SpringApplicationRunListeners、ApplicationArguments、ConfigurableEnvironment、Banner、ConfigurableApplicationContext 和 SpringBootExceptionReporter集合。

整体流程如下:

在这里插入图片描述

1、开启计时器

StopWatch stopWatch = new StopWatch();
stopWatch.start();

记录程序启动的时间为运行stopWatch.start()方法的当前时间。就整个程序的启动时间而言,其实并不精确,因为构建SpringApplication的时间并没有算进去,其中包含了从spring.factories文件中读取信息的IO操作。

2、加载运行时监听器 SpringApplicationRunListeners

SpringApplicationRunListeners listeners = getRunListeners(args);

这里只会获取到一个EventPublishingRunListener对象,此对象会贯穿整个应用程序的启动过程,用于将各阶段事件发布到相应的Spring事件监听器中。

SpringApplicationRunListner是Spring Boot应用运行时监听器,而不是Spring Boot事件监听器;在SpringApplication的运行阶段涉及的方法如下:
在这里插入图片描述

发布应用启动事件 ApplicationStartingEvent

在这里插入图片描述
进入EventPublishingRunListeners#starting()方法,使用成员变量 initialMulticaster 广播事件:
在这里插入图片描述

1> initialMulticaster成员是什么时候初始化的?

在这里插入图片描述
在初始化EventPublishingRunListener(即在调用EventPublishingRunListener构造函数)的同时会初始化initialMulticaster,并将SpringApplication中的11个Spring事件监听器添加到initialMulticaster中。

再看SimpleApplicationEventMulticaster的类图:
在这里插入图片描述

2> multicastEvent()方法发布事件

在这里插入图片描述

3> ApplicationEvent对应哪些ApplicationListener?

ApplicationStartingEvent事件为例,发布这个事件之后,应该通知哪些Spring事件监听器ApplicationListener?AbstractApplicationEventMulticaster#getApplicationListeners(ApplicationEvent,ResolvableType)方法给了我们答案。
在这里插入图片描述

在这里插入图片描述

我们再看一下supportsEvent()方法是如何判断某个Spring事件监听器是否可以处理某个Spring事件的?

以这里的ApplicationStartingEvent事件和CloudFoundryVcapEnvironmentPostProcessor事件监听器为例;
在这里插入图片描述
将Spring事件监听器封装为GenericApplicationListenerAdapter对象,然后调用GenericApplicationListenerAdapter对象的supportsSourceType(Class)方法判断是否支持监听传入的事件类型。

在这里插入图片描述
如果当前事件监听器的类型是SmartApplicationListener,则直接调用当前事件监听器的supportsEventType()方法,否则调用declaredEventType.isAssignableFrom()方法判断。

发布应用启动事件ApplicationStartingEvent之后,其实只有LoggingApplicationListener 做了实例化日志系统loggingSystem,默认情况下使用 Logback。

3、解析命令行参数

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

命令行配置源SimpleCommandLinePropertySource将命令行参数分为两组,分别为:

  1. “选项参数”,选项参数必须以“--”为前缀。
  2. “非选项参数”,非选项参数是未包含“--”前缀的命令行参数。

4、准备应用环境ConfigurableEnvironment

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments) {
	// 1、获取或者创建应用环境(创建时会根据应用类型判断)
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	// 2、配置应用环境,配置propertySource和activeProfiles
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	// 如果propertySources中没有`configurationProperties`则将`ConfigurationPropertySourcesPropertySource {name='configurationProperties'}`加入到propertySources中。
	// 有的话先移除,然后再加。
	ConfigurationPropertySources.attach(environment);
	// 3、监听器环境准备,广播ApplicationEnvironmentPreparedEvent事件
	//   这里会处理我们自定义的配置文件内容--见ConfigFileApplicationListener
	listeners.environmentPrepared(environment);
	// 4、绑定应用环境,不用往里深跟
	bindToSpringApplication(environment);
	if (!this.isCustomEnvironment) {
		// 将应用环境转换为相应应用类型的环境,比如:StandardServletEnvironment、StandardReactiveWebEnvironment、StandardEnvironment
		environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
				deduceEnvironmentClass());
	}
	// 5、防止对环境转换时有问题,这里再重新配置propertySource和activeProfiles
	ConfigurationPropertySources.attach(environment);
	return environment;
}

1)获取或者创建应用环境getOrCreateEnvironment()

private ConfigurableEnvironment getOrCreateEnvironment() {
	if (this.environment != null) {
		return this.environment;
	}
	switch (this.webApplicationType) {
	case SERVLET:
		return new StandardServletEnvironment();
	case REACTIVE:
		return new StandardReactiveWebEnvironment();
	default:
		return new StandardEnvironment();
	}
}
  1. 首先尝试获取应用环境,如果环境不存在,则根据应用类型来创建对应的环境。
  2. SERVLET应用类型创建StandardServletEnvironment、
  3. REACTIVE应用类型创建StandardReactiveWebEnvironment,
  4. 否者创建StandardEnvironment。

2)配置应用环境

configureEnvironment(environment, applicationArguments.getSourceArgs());
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {

	// 加载转换器和格式化器
	if (this.addConversionService) {
		ConversionService conversionService = ApplicationConversionService.getSharedInstance();
		environment.setConversionService((ConfigurableConversionService) conversionService);
	}
	// 配置property sources, 将“SimpleCommandLinePropertySource {name='commandLineArgs'}”添加到ConfigurableEnvironment的propertySourceList。
	configurePropertySources(environment, args);
	// 配置profiles
	configureProfiles(environment, args);
}

3)环境的propertySources中添加configurationProperties

在这里插入图片描述

此时,可以看到environment的propertySources中有6个对象,即比最初多两个对象(commandLineArgs、configurationProperties):

在这里插入图片描述

4)发布环境已整备好事件

当应用程序的环境准备之后,EventPublishingRunListener发布一个ApplicationEnvironmentPreparedEvent事件。
在这里插入图片描述

在这个阶段,ConfigFileApplicationListener事件监听器会进行yaml/properties配置文件的加载;LoggingApplicationListener事件监听器会解析日志配置,并进行日志系统的初始化

事件处理之后,environment的propertySources中有8个对象,即比上一阶段又多了多个两个对象(random、applicationConfig:[classpath:/application.yml]):
在这里插入图片描述

5、打印banner

Banner printedBanner = printBanner(environment);

6、创建Spring应用上下文

在这里插入图片描述

对于Servlet应用,这里实例化AnnotationConfigServletWebServerApplicationContext,这个过程中会实例化很多对象,包括DefaultListableBeanFactory

7、加载异常报告器SpringBootExceptionReporter

加载异常报告器SpringBootExceptionReporter,用来支持报告关于启动的错误:(若是要监测启动错误,可自定义FailureAnalysisReporter来处理具体逻辑)

exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);

在这里插入图片描述

8、Spring应用上下文刷新前准备

prepareContext(context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
		SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	// 设置应用上下文的environment
	context.setEnvironment(environment);
	// 应用上下文后置处理,默认只会将ConversionService添加到context的beanFactory中
	postProcessApplicationContext(context);
	// 应用一些初始化器,执行ApplicationContextInitializer,将所有的初始化对象放到context对象中。
	applyInitializers(context);
	// 发布 应用上下文初始化完成事件ApplicationContextInitializedEvent
	listeners.contextPrepared(context);
	if (this.logStartupInfo) {
		// 打印启动信息
		logStartupInfo(context.getParent() == null);
		// 打印active profile的信息
		logStartupProfileInfo(context);
	}
	// Add boot specific singleton beans
	// 注册启动参数bean,将容器指定的参数封装成bean,注入容器
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	// 设置banner
	if (printedBanner != null) {
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	if (beanFactory instanceof DefaultListableBeanFactory) {
		((DefaultListableBeanFactory) beanFactory)
				.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	if (this.lazyInitialization) {
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}
	// Load the sources
	// 加载所有资源(指的是启动器指定的参数)
	Set<Object> sources = getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	// 核心所在!!将我们自己的启动类bean、自动装配的内容  加载到上下文中
	load(context, sources.toArray(new Object[0]));
	// 发布 应用已准备好事件ApplicationPreparedEvent
	listeners.contextLoaded(context);
}

1)Spring应用上下文准备阶段

先将environment设置到Spring应用上下文中,然后后置处理Spring上下文将ConversionService添加到context的beanFactory中,接着执行初始化器的初始化方法,将一些后置处理器和监听器添加到Spring上下文中。接着发布Spring上下文已经完成初始化事件ApplicationContextInitializedEvent

2)Spring应用上下文装载阶段

本阶段又可划分为四个过程,分别为:“注册Spring Boot Bean”、“合并Spring应用上下文配置源”、“加载Spring应用上下文配置源” 和 “发布应用已准备好事件ApplicationPreparedEvent”。

1> 注册Spring Boot Bean

将之前创建的ApplicationArguments对象和可能存在的Banner实例注册为Spring 单例Bean。
在这里插入图片描述

2> 合并Spring应用上下文配置源

Set<Object> sources = getAllSources();
public Set<Object> getAllSources() {
	Set<Object> allSources = new LinkedHashSet<>();
	// primarySources来自于SpringApplication构造器参数
	if (!CollectionUtils.isEmpty(this.primarySources)) {
		allSources.addAll(this.primarySources);
	}
	// sources来自于setSource(Set)方法
	if (!CollectionUtils.isEmpty(this.sources)) {
		allSources.addAll(this.sources);
	}
	return Collections.unmodifiableSet(allSources);
}
 
  

3> 加载Spring应用上下文配置源

load(context, sources.toArray(new Object[0]));
protected void load(ApplicationContext context, Object[] sources) {
	if (logger.isDebugEnabled()) {
		logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
	}
	// 获取bean对象定义的加载器
	BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
	if (this.beanNameGenerator != null) {
		loader.setBeanNameGenerator(this.beanNameGenerator);
	}
	if (this.resourceLoader != null) {
		loader.setResourceLoader(this.resourceLoader);
	}
	if (this.environment != null) {
		loader.setEnvironment(this.environment);
	}
	// 实际执行load的是BeanDefinitionLoader中的load方法
	loader.load();
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
ConditionEvaluator.shouldSkip(AnnotatedTypeMetadata)方法会判断当前要注册的类有没有被@Conditional注解标注?是否应该跳过它的注册逻辑?

BeanDefinitionReaderUtils.registerBeanDefinition(BeanDefinitionHolder, BeanDefinitionRegistry);方法中真正将当前启动类注册为BeanDefinition,并且其中会涉及到Alias别名的处理。
在这里插入图片描述

这个过程默认情况下只是将启动类的BeanDefinition注册到Spring上下文中。

4> 发布应用已准备完事件ApplicationPreparedEvent

listeners.contextLoaded(context);

你可能感兴趣的:(Spring,Boot,源码,spring,spring,boot)