Spring Boot原理及启动流程

Spring Boot引导程序是Spring Boot关键,从启动过程一探Spring Boot原理。

以IDE环境下的Spring Boot应用启动为例。

DemoApplication.java

SpringApplication.run(DemoApplication.class, args);

以下是启动流程中的几个重要节点:

查看运行环境

查看应用属于哪种类型:

SERVLET(Web应用)、REACTIVE(Reactive Web应用)、NONE(标准应用)。

根据类型使用不同的spring application context。

SERVLET、REACTIVE、NONE 分别对应如下三种application context:

org.springframework.web.context.WebApplicationContex; 
org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext;
org.springframework.web.context.ConfigurableWebApplicationContext;

具体实现可看WebApplicationType:

org.springframework.boot.WebApplicationType.deduceFromClasspath;

应用默认配置

Spring Boot内置许多默认配置项,启动过程会加载两类重要配置项:

org.springframework.context.ApplicationContextInitializer;
org.springframework.context.ApplicationListener

分别用于:初始化和发布事件。

两类配置项对应的清单,可从如下文件中找到:

spring-boot-x.y.z.jar META-INF/spring.factories spring-boot-autoconfigure-x.y.z.jar META-INF/spring.factories spring-beans-x.y.z.jar META-INF/spring.factories

通常包含以下类:

ApplicationContextInitializer

[org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,
org.springframework.boot.context.ContextIdApplicationContextInitializer,
org.springframework.boot.context.config.DelegatingApplicationContextInitialize,
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer,
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener]

ApplicationListener

​​​​[org.springframework.boot.context.config.ConfigFileApplicationListener,
org.springframework.boot.context.config.AnsiOutputApplicationListener,
org.springframework.boot.context.logging.LoggingApplicationListener,
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,
org.springframework.boot.autoconfigure.BackgroundPreinitializer,
org.springframework.boot.context.config.DelegatingApplicationListener,
org.springframework.boot.builder.ParentContextCloserApplicationListener,
org.springframework.boot.ClearCachesApplicationListener,
org.springframework.boot.context.FileEncodingApplicationListener,
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener]

Spring Boot使用如下API获取清单上的Class并创建实例。

SpringApplication.getSpringFactoriesInstances

若有自定义配置项加入到此阶段,并对执行顺序有要求,使用如下注解:

org.springframework.core.annotation.Order

确定应用启动入口

启动程序获取当前线程的调用栈,遍历调用栈找到以main命名的方法,并返回main方法所在的Class,确定启动程序入口。

StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();

具体代码参见SpringApplication类:

org.springframework.boot.SpringApplication.deduceMainApplicationClass

配置Headless项

J2SE 1.4为awt包加入了java.awt.headless选项,防止java.awt.*包下的某些类,运行在没显示器和键盘的设备上抛出HeadlessException异常。

默认值为true。

java.awt.headless=true

具体参考Oracle关于headless的说明

发布启动事件

启动过程的各类事件,由SpringApplicationRunListeners统一管理,此时会发布一个启动事件:

org.springframework.boot.context.event.ApplicationStartingEvent;

默认由EventPublishingRunListener将事件分发到各个Listener:

org.springframework.boot.context.event.EventPublishingRunListener

默认使用当前线程去发布事件,也就是说,监听器有机会阻塞启动过程。

如果,配置了Executor,事件会由Executor异步执行,使用异步通常有两种子类可选:

org.springframework.core.task.SimpleAsyncTaskExecutor; org.springframework.core.task.SyncTaskExecutor;

注:执行前会统一调用GenericApplicationListener.supportsSourceType方法,过滤掉与事件无关的监听器。

读取应用程序配置数据

根据应用类型,创建对应的环境配置对象:

SpringApplication.getOrCreateEnvironment

标准的环境对象,将配置项分在四个命名空间下:

StandardEnvironment {activeProfiles=[], defaultProfiles=[default],
propertySources=[PropertiesPropertySource {name='systemProperties'},
SystemEnvironmentPropertySource {name='systemEnvironment'}]}

配置默认支持的配置数据转换类型,通过以下类型转换器确定:

org.springframework.core.convert.support.DefaultConversionService;
org.springframework.boot.convert.ApplicationConversionService;

发布环境配置事件

Spring Boot读取配置文件时,会发出ApplicationEnvironmentPreparedEvent事件:

org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent

监听此事件的监听器有机会通过getEnvironment方法拿到配置数据。

设置日志项

此阶段Spring Boot允许给日志加点料,改变日志字体颜色,以彩色输出为例:

spring.output.ansi.enabled=ALWAYS

绑定环境变量数据

Spring提供的配置数据绑定方式多到眼花缭乱,此处不再细说。

Banner输出

启动过程中经典的spring boot图案,就在此时由SpringBootBanner输出:

org.springframework.boot.SpringBootBanner

可在resource目录下放置图片做为logo,Spring Boot会对图片进行转换。

文件命名格式:banner.[gif, jpg, png]。

也可通过spring.banner.image.location指定图片路径。

或使用banner.txt放置文字。

具体参考如下两个类:

org.springframework.boot.SpringApplicationBannerPrinter org.springframework.boot.ImageBanner

创建ApplicationContext

熟悉的spring味道,根据环境提供三种不同的上下文对象:

org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext(web项目)org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext(ReactiveWeb项目)org.springframework.context.annotation.AnnotationConfigApplicationContext(标准环境,非web项目)

异常处理机制

此阶段建立起异常处理机制,负责异常处理的是如下类:

org.springframework.boot.diagnostics.FailureAnalyzers

初始化应用程序

此阶段会初始化应用程序上下文对象。

META-INF/spring.factories配置文件中默认的一系统初始化类粉墨登场。

发布初始化事件

初始化完成会发布初始化事件:

org.springframework.boot.context.event.ApplicationContextInitializedEvent

监听者有机会通过getApplicationContext获取应用程序上下文。

加载应用程序

此时,会将应用程序入口类加入到Spring容器中。此示例为:

DemoApplication

发布应用启动事件

启动应用会发布启动事件:

org.springframework.boot.context.event.ApplicationPreparedEvent

监听者通过getApplicationContext方法能够拿到应用程序上下文对象。如:

org.springframework.context.annotation.AnnotationConfigApplicationContext;

开启依赖注入之旅

经典依赖注入就发生在此时,此阶段开始对各个spring bean打针。

事件通知

ApplicationStartingEvent发起ContextRefreshedEvent事件,通知监听者依赖注入之旅结束。

ApplicationStartingEvent发起ApplicationStartedEvent事件,通知监听者应用程序启动完成。

ApplicationStartingEvent发起ApplicationReadyEvent事件,通知监听者应用程序已就绪。

注:应用程序自然关闭时会发布ContextClosedEvent事件。