0%

Spring Boot 启动流程简记

代码来自于Spring Boot 2.0.4.RELEASE

SpringApplication类负责由main方法启动spring应用,文档中给出了启动的主要步骤:

  • 创建ApplicationContext
  • 处理命令行参数
  • refresh ApplicationContext,加载bean
  • 触发各类Runner

SpringApplication的初始化

main函数中通过SpringApplication类的静态方法run()开始流程,SpringApplication类首先将main函数所在的类作为primarySource来初始化一个SpringApplication的实例,作为primarySource是指“load beans from the specified primary sources”,SpringApplication实例化包括的流程有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// resourceLoader用于从classPath或者文件系统加载资源文件
// ResourceLoader这个类是一个策略模式的接口,有ApplicationContext、DefaultResourceLoader等多个实现
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 加载primarySources,因为是一个Set所以可看出有多个sources,SpringApplication类文档也说明了可以进行设置
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断应用类型,默认是Servlet
this.webApplicationType = deduceWebApplicationType();
// 读取‘META-INF/spring.factories’中的接口的实现的配置,来初始化ApplicationContextInitializer
// ApplicationContextInitializer可以在ApplicationContext初始化过程中、refresh之前注册一系列回调来进行一些操作,比如配置文件的操作
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 通过堆栈信息推断main函数所在类
this.mainApplicationClass = deduceMainApplicationClass();

run()启动流程

SpringApplication初始化完成后,调用SpringApplication类的run()方法进入启动流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public ConfigurableApplicationContext run(String... args) {
// 启动StopWatch,用于计时
// “allowing for timing of a number of tasks, exposing total running time and running time for each named task.”
// 启动完成时打印的“Started *** in *** seconds”就是StopWatch负责计时的
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
// 报告启动过程中异常的回调,可自定义
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置java.awt.headless属性
// headless模式用于缺少显示屏、鼠标、键盘等情况下时,仍然可以使用graphic、canvas等
configureHeadlessProperty();
// 通过读取‘META-INF/spring.factories’来初始化SpringApplicationRunListener
// 默认只有一个EventPublishingRunListener,负责发布SpringApplicationEvent,实现了environmentPrepared、contextPrepared、contextLoaded、starting、started等的回调
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 读取参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// prepareEnvironment,创建和配置environment
ConfigurableEnvironment environment =
prepareEnvironment(listeners,
applicationArguments);
// 设置spring.beaninfo.ignore,默认是true
configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = printBanner(environment);
// 创建ApplicationContext
// createApplicationContext()方法是protected的,可以自定义,也可以使用setApplicationContextClass(Class)设置
// 会先尝试使用自定义的“set application context or application contextclass”,没有使用setApplicationContextClass(Class)设置时,这一方法会通过反射创建一个默认的ApplicationContext
context = createApplicationContext();
// 通过SpringFactories初始化exceptionReporters
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 配置ApplicationContext
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 刷新ApplicationContext,初始化tomcat、加载bean、启动tomcat
refreshContext(context);
// protected方法,可以自定义子类来作为刷新完成的回调
afterRefresh(context, applicationArguments);
// 计时结束
stopWatch.stop();
// 输出启动过程的计时信息等(Started MonitorcenterApplication in *** seconds (JVM running for ***))
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 通知listener started
listeners.started(context);
// 调用各个runner,如ApplicationRunner、CommandLineRunner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}

try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

prepareEnvironment——创建和配置environment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置environment的Property和Profile
// 相关的函数configureEnvironment中调用的configurePropertySources、configureProfiles都是protected的,可以Override them for complete control over Environment customization
// 此处处理了servertContext、system、cmd等property,还未加载application.properties之类文件的proterty
// SpringApplication默认实现中,在configurePropertySources()方法中尝试添加defaultProperties(addLast)和commandLineArgs(addFirst,所以命令行指定的properties优先级高),在configureProfiles()方法尝试确定active的profile(比如命令行指定了spring.profiles.active时)
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 监听器通知environmentPrepared
// 其中,ConfigFileApplicationListener的回调中,通过‘META-INF/spring.factories’加载所有EnvironmentPostProcessor,并且自身也实现了EnvironmentPostProcessor并加入,然后调用所有EnvironmentPostProcessor;ConfigFileApplicationListener的实现中添加了random properties,然后load了application.properties等配置文件
// 还有配置日志的LoggingApplicationListener等listenter
listeners.environmentPrepared(environment);
// 绑定SpringApplication的environment即运行环境
bindToSpringApplication(environment);
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
// 在environment上添加configurationProperties,放在第一个位置(首先被读取的),内容是已经添加的propertySources(查询配置时是在列表中从前往后找的)
ConfigurationPropertySources.attach(environment);
return environment;
}

ConfigFileApplicationListener加载配置

ConfigFileApplicationListener首先加载了random配置,然后开始加载配置文件:

  • 读取通过环境变量等配置的active的profile;首先查找environment之前的配置(命令行、环境变量等)有无设置“spring.profiles.active”或”spring.profiles.include”,已经设置时将指定的profile下的property读入;将其他形式设置的active的profile下的property也读入;如果此时未读到active的profile,读取默认的配置文件,默认的配置文件可以使用”spring.profiles.default”设置,默认是“default”
  • 从file:./config/、file:./、classpath:/config/、classpath:/application.properties四个默认路径(也可以自定义searchLocation)查找properties、xml、yml、yaml类型的配置文件,四个路径也按这一顺序形成优先级;加载active的配置文件和默认的配置文件(整个过程的代码比较多、比较绕,可见https://blog.csdn.net/weixin_30635053/article/details/101944096)

最终,配置文件在list的顺序是:active的profile -> 默认的profile

ConfigFileApplicationListener还在onApplicationPreparedEvent事件时添加了PropertySourceOrderingPostProcessor,将defaultProperties移动到list最后

prepareContext——配置ApplicationContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner){
// 设置使用的environment
context.setEnvironment(environment);
// 一些配置,默认的有beanNameGenerator、resourceLoader
// 是protected方法,可以添加自定义配置
postProcessApplicationContext(context);
// 调用各个ApplicationContextInitializer(在初始化SpringApplication时通过spring.factories加载)
// 比如ServerPortInfoApplicationContextInitializer负责“sets Environment properties for the ports that {@link WebServer} servers are actually listening on”
applyInitializers(context);
// 通知各个监听器contextPrepared,回调
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}

// 添加applicationArguments作为一个bean
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner",printedBanner);
}

// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 向ApplicationContext加载必要的几个BeanDefinition
load(context, sources.toArray(new Object[0]));
// 通知listener contextLoaded
// 注册了ApplicationContextAware接口的listener会回调setApplicationContext
// 广播ApplicationPreparedEvent
listeners.contextLoaded(context);
}

load——向ApplicationContext加载BeanDefinition

load中通过扫描注解从之前的primarySource加载BeanDefinition(也可以设置source)

1
2
3
4
5
// 查找注解
if (isComponent(source)) {
this.annotatedReader.register(source);
return 1;
}

其中,扫描注解使用的是AnnotationUtils类

refreshContext——刷新ApplicationContext,加载bean

最终调用了AbstractApplicationContext类的refresh()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// 使用startupShutdownMonitor加锁
// 对startup和close过程中的refresh和destory bean的过程加锁
synchronized (this.startupShutdownMonitor) {
// prepareRefresh
// 留下了一个protected的initPropertySources()方法,可以实现子类来Replace any stub property sources with actual instances.
// 然后验证标记为required的property
// Prepare this context for refreshing.
prepareRefresh();

// 初始化bean factory
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// 设置ClassLoader、ignoreDependency等
// 注册early post-processor for detecting inner beans as ApplicationListeners
// 注册environment、systemProperties、systemEnvironment bean
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

try {
// protected方法,可以实现动态的添加、修改bean等操作
// “Modify the application context's internal bean factory after its standard initialization.”
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);

// 调用各个BeanFactoryPostProcessor
// 调用BeanDefinitionRegistryPostProcessor来加载所有的BeanDefination,比如使用ConfigurationClassPostProcessor来加载所有@Configuration的类
// 比如,ConfigFileApplicationListener在onApplicationPreparedEvent事件时添加了PropertySourceOrderingPostProcessor,如果defaultProperties存在,此时将其移动到list的最后
// BeanFactoryPostProcessor是在加载了BeanDefination之后、bean实例化之前调用的
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);

// 调用所有BeanPostProcessor
// 在bean实例化之后执行,提供了两个回调
// postProcessBeforeInitialization(),在bean实例化之后、初始化之前调用
// postProcessAfterInitialization(),在bean实例化之后、初始化之后调用
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);

// MessageSource,用于国际化等
// Initialize message source for this context.
initMessageSource();

// Initialize event multicaster for this context.
initApplicationEventMulticaster();

// onRefresh(),protected方法,可以在子类中实现initialize other special beans
// 对于servlet应用,实现的子类中调用了createWebServer()来读取配置、创建Tomcat web server
// Initialize other special beans in specific context subclasses.
onRefresh();

// Check for listener beans and register them.
registerListeners();

// 初始化bean
// 最终调用了beanFactory.preInstantiateSingletons()
// 调用bean的初始化方法initialization,以及所有实现了SmartInitializingSingleton接口的初始化后的回调post-initialization callback
// 实现了Aware接口的bean在这一阶段实例化之后执行set***Aware()回调
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// 初始化及调用lifecycleProcessor
// 发布ContextRefreshedEvent事件
// 对于servlet应用,开始startWebServer(),并发布ServletWebServerInitializedEvent事件
// Last step: publish corresponding event.
finishRefresh();
}
// 以下代码省略
...

关于BeanFactoryPostProcessor和BeanPostProcessor

  • BeanFactoryPostProcessor在加载了BeanDefination之后、实例化之前调用,可以用于修改未实例化的bean的各种属性
  • BeanPostProcessor在实例化之后调用,提供了初始化之前和初始化之后两个回调:postProcessBeforeInitialization()、postProcessAfterInitialization()
  • 实例化对应的方法是:如果bean实现了InitializingBean接口,对应的方法为afterPropertiesSet;定义bean时init-method指定的方法

要注意,在这里:

  • 实例化是Instantiation,是实例化一个bean
  • 初始化是Initialization,是封装bean

先Instantiation在Initialization,在调用postProcessBeforeInitialization()时,bean中各项@Autowired的依赖已经注入了

对于实现了Aware接口的bean,如EnvironmentAware、ResourceLoaderAware、ApplicationContextAware等,在实例化之后、初始化之前的会通过BeanPostProcessor的postProcessBeforeInitialization()执行set***Aware()回调方法

整个过程中,listener触发的回调有:starting->environmentPrepared->contextPrepared->contextLoaded->started->running

发送的ApplicationEvent主要有:ApplicationStartingEvent->ApplicationEnvironmentPreparedEvent->ApplicationPreparedEvent->ContextRefreshedEvent->ServletWebServerInitializedEvent->ApplicationStartedEvent->org.springframework.boot.context.event.ApplicationReadyEvent