注:Spring Boot版本:2.3.7
SpringApplication准备阶段会依次准备一些核心对象:SpringApplicationRunListeners、ApplicationArguments、ConfigurableEnvironment、Banner、ConfigurableApplicationContext 和 SpringBootExceptionReporter集合。
整体流程如下:
StopWatch stopWatch = new StopWatch();
stopWatch.start();
记录程序启动的时间为运行stopWatch.start()
方法的当前时间。就整个程序的启动时间而言,其实并不精确,因为构建SpringApplication的时间并没有算进去,其中包含了从spring.factories
文件中读取信息的IO操作。
SpringApplicationRunListeners listeners = getRunListeners(args);
这里只会获取到一个EventPublishingRunListener
对象,此对象会贯穿整个应用程序的启动过程,用于将各阶段事件发布到相应的Spring事件监听器中。
SpringApplicationRunListner
是Spring Boot应用运行时监听器,而不是Spring Boot事件监听器;在SpringApplication的运行阶段涉及的方法如下:
进入EventPublishingRunListeners#starting()
方法,使用成员变量 initialMulticaster
广播事件:
initialMulticaster
成员是什么时候初始化的?
在初始化EventPublishingRunListener
(即在调用EventPublishingRunListener构造函数)的同时会初始化initialMulticaster
,并将SpringApplication中的11个Spring事件监听器添加到initialMulticaster
中。
再看SimpleApplicationEventMulticaster
的类图:
multicastEvent()
方法发布事件以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。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
命令行配置源SimpleCommandLinePropertySource
将命令行参数分为两组,分别为:
- “选项参数”,选项参数必须以“
--
”为前缀。- “非选项参数”,非选项参数是未包含“
--
”前缀的命令行参数。
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;
}
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();
}
}
- 首先尝试获取应用环境,如果环境不存在,则根据应用类型来创建对应的环境。
- SERVLET应用类型创建StandardServletEnvironment、
- REACTIVE应用类型创建StandardReactiveWebEnvironment,
- 否者创建StandardEnvironment。
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);
}
此时,可以看到environment的propertySources中有6个对象,即比最初多两个对象(commandLineArgs、configurationProperties):
当应用程序的环境准备之后,EventPublishingRunListener发布一个ApplicationEnvironmentPreparedEvent
事件。
在这个阶段,ConfigFileApplicationListener事件监听器会进行yaml/properties配置文件的加载;LoggingApplicationListener事件监听器会解析日志配置,并进行日志系统的初始化。
事件处理之后,environment的propertySources中有8个对象,即比上一阶段又多了多个两个对象(random、applicationConfig:[classpath:/application.yml]):
Banner printedBanner = printBanner(environment);
对于Servlet应用,这里实例化AnnotationConfigServletWebServerApplicationContext
,这个过程中会实例化很多对象,包括DefaultListableBeanFactory
。
加载异常报告器SpringBootExceptionReporter,用来支持报告关于启动的错误:(若是要监测启动错误,可自定义FailureAnalysisReporter
来处理具体逻辑)
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
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);
}
先将environment设置到Spring应用上下文中,然后后置处理Spring上下文将ConversionService添加到context的beanFactory中,接着执行初始化器的初始化方法,将一些后置处理器和监听器添加到Spring上下文中。接着发布Spring上下文已经完成初始化事件ApplicationContextInitializedEvent
本阶段又可划分为四个过程,分别为:“注册Spring Boot Bean”、“合并Spring应用上下文配置源”、“加载Spring应用上下文配置源” 和 “发布应用已准备好事件ApplicationPreparedEvent”。
将之前创建的ApplicationArguments对象和可能存在的Banner实例注册为Spring 单例Bean。
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);
}
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上下文中。
listeners.contextLoaded(context);