引言
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);
}
}