Spring Boot启动流程
一.前言
本文主要讲解spring boot应用启动的流程,看spring boot在启动过程中,如何实现以下几个非常重要的过程:
- 如何决定是web应用
- 创建spring环境
- 创建上下文
对于每个步骤的原理细节,本文不详细介绍。本文旨在探索宏观的流程控制。
二.整体流程
众所周知,Java应用的启动入口在于包含main方法的主类中,对于传统的web应用,包含main方法的主类由web容器实现。应用开发者只要遵循web开发规范,而不需要关心main方法主类。
spring boot应用恰好相反,spring boot应用的main方法主类由应用开发者编写,主要利用其SpringApplication快捷启动项目。
SpringApplication主要是帮助引导和启动spring boot应用,它本身提供很多配置方法用于设置spring应用。在启动过程中SpringApplication中主要完成以下几个步骤:
- 初始化SpringApplication
- 创建合适的spring环境
- 打印banner
- 依赖类路径创建合适的ApplicationContext
- 注册CommandLinePropertySource,作为命令行参数属性源
- 刷新上下文,载入单例Bean
- 以上的每个阶段,触发监听器的相应阶段
- 如果启动失败,进行失败分析
接下来看下SpringApplication的几个主步骤:
1.初始化SpringApplication
@SuppressWarnings({ "unchecked", "rawtypes" })
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
这个initialize方法中主要是将配置的源类加入Set集合,决定是否为web环境、设置上下文初始化器、设置上下文监听器、mainApplicationClass。
2.SpringApplication run
public ConfigurableApplicationContext run(String... args) {
// 创建StopWatch,用于统计spring boot应用的启动时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 定义applicationContext,失败分析器analyzers
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
// 加载SpringApplicationRunListener,主要用于监听spring boot应用run过程的每个主要阶段
SpringApplicationRunListeners listeners = getRunListeners(args);
// 监听spring应用run过程的start开始阶段
listeners.started();
try {
// 创建ApplicationArguments,抽象保存应用main方法参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 准备Environment
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 打印Banner
Banner printedBanner = printBanner(environment);
// 创建ApplicationContext
context = createApplicationContext();
// 创建失败分析器
analyzers = new FailureAnalyzers(context);
// 上下文准备,主要是配置上下文。刷新的前置
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 刷新上下文,就是加载bean
refreshContext(context);
// 刷新的后置
afterRefresh(context, applicationArguments);
// 监听spring应用run过程的finish完成阶段
listeners.finished(context, null);
// stopWatch停止,代表spring应用启动完成,完成启动的时间统计
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
以上的run方法是spring boot应用启动的核心流程,启动的逻辑控制完全在该run中实现。
接下来就细细剖析其中的每个阶段。
三.如何决定Web应用
spring boot是根据类路径是否存在web应用的标志类来判断是否为web应用。所以,这点对于日常开发项目时,maven的依赖要求非常严格,务必不能混乱引用依赖,同事也要注意依赖传递等。
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
如果类路径上有以上两个类,spring boot则认为该应用是为web应用。
private boolean deduceWebEnvironment() {
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return false;
}
}
return true;
}
如果Servlet和ConfigurableWebApplicationContext都在类路径上,即认为是web项目。
四.创建spring环境
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 创建spring环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置spring环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 触发监听器环境准备阶段
listeners.environmentPrepared(environment);
if (isWebEnvironment(environment) && !this.webEnvironment) {
environment = convertToStandardEnvironment(environment);
}
return environment;
}
spring环境准备由SpringApplication中的prepareEnvironment完成,其中抉择环境时,会根据是否为web应用选择不同的环境类型:
- StandardEnvironment:标准的spring环境,其中包含了profiles和spring propertySources
- StandardServletEnvironment:web spring环境,是StandardEnvironment的扩展,其中增加了servletContext propertySource和servletConfig propertySource
其中配置spring环境时,会将命令行的参数和prfile解析进入spring环境的propertySource和profiles中。
五.创建上下文
protected ConfigurableApplicationContext createApplicationContext() {
// 如果SpringApplication中的applicationContextClass不为空,直接返回applicationContextClass
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 依据web环境,抉择上下文类型
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
// 格局类型,反射实例化上下问类
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
spring boot抉择上下文由SpringApplication中的createApplicationContext实现,其中只有两种上下文形式:
-
DEFAULT_WEB_CONTEXT_CLASS:嵌入式的web上下文AnnotationConfigEmbeddedWebApplicationContext。该上下文由spring boot实现,其中包含了嵌入式容器的创建,初始化等逻辑
-
DEFAULT_CONTEXT_CLASS:注解式配置上下文AnnotationConfigApplicationContext,该上下文是spring框架中实现,主要用于注解式配置的spring应用