引言
SpringBoot是一个广泛使用的Java框架,旨在简化基于Spring框架的应用程序的开发过程。在这篇文章中,我们将深入探讨SpringBoot应用程序的启动流程,了解其背后的机制。
Spring Boot 启动概览
SpringBoot应用程序的启动通常从一个包含 main 方法的类开始,例如:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
这里的SpringApplication.run是启动SpringBoot应用程序的入口。接下来,我们将深入分析这个方法及其调用的各个阶段。
SpringApplication 类详解
SpringApplication类是SpringBoot应用程序启动过程的核心类。它负责初始化应用程序的上下文,加载配置,启动嵌入式服务器等。
SpringApplication构造函数
SpringApplication类有多个构造函数,常用的是接收一个或多个Class<?> 参数的构造函数:
public SpringApplication(Class<?>... primarySources) {
this.setInitializers((Collection) this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners((Collection) this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
这里设置了初始化器和监听器,并推断出主应用程序类。
run 方法
SpringApplication.run方法是启动SpringBoot应用程序的入口:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
这个方法内部调用了SpringApplication实例的run方法:
public ConfigurableApplicationContext run(String... args) {
// 初始化阶段
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
// 环境准备阶段
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
// 上下文创建阶段
Banner printedBanner = this.printBanner(environment);
ConfigurableApplicationContext context = this.createApplicationContext();
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 上下文刷新阶段
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
listeners.finished(context, null);
// 应用程序运行阶段
this.callRunners(context, applicationArguments);
return context;
}
源码解析
准备阶段
在准备阶段中,Spring Boot 会加载应用程序的初始设置,并创建 Spring Boot 上下文。这个阶段的核心源码是 SpringApplication
类的 run()
方法,它会调用 Spring Boot 的各个初始化器进行初始化和准备工作。
public ConfigurableApplicationContext run(String... args) {
// 启动计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 定义应用程序上下文和异常报告器列表
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置 Headless 属性
configureHeadlessProperty();
// 获取 Spring Boot 启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 执行启动监听器的 starting 方法
listeners.starting();
try {
// 解析命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备应用程序环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 配置忽略 BeanInfo
configureIgnoreBeanInfo(environment);
// 打印 Banner
Banner printedBanner = printBanner(environment);
// 创建应用程序上下文
context = createApplicationContext();
// 获取异常报告器,关于异常报告,我下次专门讲一下SpringBoot 的异常收集器。
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 准备应用程序上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新应用程序上下文
refreshContext(context);
// 刷新后操作
afterRefresh(context, applicationArguments);
// 停止计时器
stopWatch.stop();
// 记录启动日志
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 执行启动监听器的 started 方法
listeners.started(context);
// 执行 Runner
callRunners(context, applicationArguments);
} catch (Throwable ex) {
// 处理启动失败
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 执行启动监听器的 running 方法
listeners.running(context);
} catch (Throwable ex) {
// 处理启动失败
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
// 返回应用程序上下文
return context;
}
在 run()
方法中,Spring Boot 首先会创建一个 StopWatch 对象,用于记录整个启动过程的耗时。然后,Spring Boot 会调用 getRunListeners(args)
方法获取 Spring Boot 的各个启动监听器,并调用starting()
方法通知这些监听器启动过程已经开始。接着调用 prepareEnvironment(listeners, applicationArguments)
方法创建应用程序的环境变量。
这个方法会根据用户的配置和默认设置创建一个 ConfigurableEnvironment对象
,并将其传给后面的 createApplicationContext()
方法。printBanner(environment)
方法打印启动界面的 Banner,调用 refreshContext(context)
方法刷新上下文。这个方法会启动上下文,执行各种启动任务,包括创建 Web 服务器、加载应用程序的配置、初始化各种组件等。具体的启动任务会在刷新上下文阶段中进行。
应用上下文创建阶段
在应用上下文创建阶段中,Spring Boot 会创建应用程序的上下文,包括各种配置信息、Bean 的加载和初始化等。这个阶段的核心源码是 Spring Boot 自动配置机制,通过扫描 classpath 中的配置文件,自动加载和配置各种组件和 Bean。
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable to create a default ApplicationContext, " +
"please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
在 createApplicationContext()
方法中,Spring Boot 首先会判断应用程序的类型,如果是 Web 应用程序,则会创建一个 WebApplicationContext
;否则,会创建一个普通的 ApplicationContext
。调用 BeanUtils.instantiateClass(contextClass)
方法创建应用程序的上下文。这个方法会根据上面的逻辑创建一个相应的 ApplicationContext
。调用 load()
方法加载应用程序的配置。
这个方法会扫描 classpath 中的各种配置文件,例如 application.properties
、application.yml
、META-INF/spring.factories
等,自动配置各种组件和 Bean。调用 postProcessApplicationContext()
方法对应用程序的上下文进行后处理。这个方法会调用各种初始化器和监听器,执行各种初始化任务。
刷新上下文阶段
在刷新上下文阶段中,Spring Boot 会执行各种启动任务,包括创建 Web 服务器(刚才我们跟源码的时候也看到了,如上我的截图)、加载应用程序的配置、初始化各种组件等。这个阶段的核心源码是 Spring Boot 的刷新机制,它会调用各种初始化器和监听器,执行各种启动任务。
protected void refreshContext(ConfigurableApplicationContext applicationContext) {
refresh(applicationContext);
if (this.registerShutdownHook) {
try {
applicationContext.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
在 refreshContext()
方法中调用 refresh(applicationContext)
方法刷新上下文。这个方法是 ApplicationContext
接口的核心方法,会启动上下文,执行各种启动任务。调用 registerShutdownHook()
方法注册应用程序的关闭钩子。这个方法会在应用程序关闭时自动执行,清理资源、关闭线程等,所以我们利用此特性在服务关闭的时候清理一些资源。并向外部发送告警通知。
在 refresh(applicationContext)
方法中,Spring Boot 会执行上下文的各种启动任务,包括创建 Web 服务器、加载应用程序的配置、初始化各种组件等。具体的启动任务会调用各种初始化器和监听器,例如:
for (ApplicationContextInitializer<?> initializer : getInitializers()) {
initializer.initialize(applicationContext);
}
另外,Spring Boot 还会调用各种监听器,我们不做赘述,例如:
for (ApplicationListener<?> listener : getApplicationListeners()) {
if (listener instanceof SmartApplicationListener) {
SmartApplicationListener smartListener = (SmartApplicationListener) listener;
if (smartListener.supportsEventType(eventType)
&& smartListener.supportsSourceType(sourceType)) {
invokeListener(smartListener, event);
}
}
else if (supportsEvent(listener, eventType)) {
invokeListener(listener, event);
}
}