SpringBoot应用启动流程解析

SpringBoot应用启动流程解析

​ 每个使用 Spring Boot 的开发者都极为熟悉的入口:SpringApplication.run()。这个简单的静态方法调用,背后隐藏着极其精妙和复杂的启动逻辑。文本文将围绕两大核心部分展开:SpringApplication 的构造阶段run 方法的执行阶段

1、SpringApplication 的构造

​ 当我们调用 SpringApplication.run(Application.class, args) 时,实际上首先创建了一个 SpringApplication 实例。这个构造过程是整个启动流程的“准备阶段”,它加载了一系列至关重要的配置信息。让我们看看 SpringApplication 的构造方法里发生了什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 1. 保存主配置类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 2. 推断Web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 3. 加载引导注册初始化器
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 4. 加载应用上下文初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 5. 加载应用监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 6. 推断主入口类
this.mainApplicationClass = deduceMainApplicationClass();
}

1.1 主配置类 (Primary Sources)

构造方法的第一件事,就是将传入的 primarySources(通常是带有 @SpringBootApplication 注解的类)保存到一个 LinkedHashSet 中。LinkedHashSet 的使用保证了元素的唯一性和有序性。这为后续的组件扫描和自动配置提供了最核心的配置源。

1.2 推断 Web 应用类型

WebApplicationType.deduceFromClasspath() 这一步非常关键。它通过检查类路径中是否存在特定的类,来判断当前应用的类型:

  • REACTIVE: 存在 DispatcherHandler 但不存在 DispatcherServletServletContainer 时,判定为响应式 Web 应用。
  • SERVLET: 存在 javax.servlet.ServletConfigurableWebApplicationContext 时,判定为传统的 Servlet Web 应用。
  • NONE: 如果上述条件均不满足,则为非 Web 应用。

这个判断结果直接决定了后面创建什么样的 ApplicationContext 和嵌入式 Web 服务器。

1.3 加载 Spring 工厂 (spring.factories)

这是 Spring Boot 最核心的 SPI (Service Provider Interface) 机制。getSpringFactoriesInstances 方法会去加载所有 META-INF/spring.factories 文件,并根据传入的类型(如 ApplicationContextInitializer.class)获取对应的实现类全名,然后实例化它们。

这个过程加载了三种关键的组件:

  • BootstrapRegistryInitializer: 用于初始化引导注册表,这是在 Spring Boot 2.4.0 引入的新特性,主要用于更早期的启动阶段,例如 Spring Cloud 的引导上下文初始化。
  • ApplicationContextInitializer: 这是 Spring 框架原生的回调接口,允许我们在 ConfigurableApplicationContext 执行 refresh() 之前对其进行自定义设置。
  • ApplicationListener: 加载各种应用事件监听器,它们会监听 SpringApplication 在启动不同阶段发布的事件,实现解耦和扩展。

1.4 推断主入口类

deduceMainApplicationClass() 通过分析当前线程的调用堆栈,找到包含 main 方法的那个类。这个类通常就是我们的入口类,它将被用于日志打印和一些内部逻辑判断。

至此,SpringApplication 对象初始化完毕,它已经收集了所有必要的“弹药”,准备开始真正的启动冲锋。

2、核心发动机 - run 方法的执行

SpringApplication.run(String... args) 方法是整个启动流程的“总控室”。它的代码结构清晰,步骤分明。我们将深入每一个步骤,看看它们究竟做了什么。

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
public ConfigurableApplicationContext run(String... args) {
// 1. 启动时间统计
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 2. 创建引导上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 3. 配置Headless属性
configureHeadlessProperty();
// 4. 获取并启动运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 5. 创建并准备环境(Environment)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
// 6. 打印Banner
Banner printedBanner = printBanner(environment);
// 7. 创建应用上下文(ApplicationContext)
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 8. 准备上下文
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 9. 刷新上下文(核心中的核心)
refreshContext(context);
// 10. 刷新后置处理
afterRefresh(context, applicationArguments);
// 11. 停止计时,打印启动日志
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 12. 发布应用已启动事件
listeners.started(context);
// 13. 调用运行器(Runners)
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// ... 异常处理 ...
}
// 14. 发布应用已就绪事件
listeners.running(context);
return context;
}

2.1 启动引导与监听机制

  • StopWatch: 一个简单的计时器,精确记录 Spring Boot 启动的总耗时。
  • createBootstrapContext(): 创建 DefaultBootstrapContext,并调用之前加载的 BootstrapRegistryInitializer 对其进行初始化。这为应用启动的最早期提供了一个脱离 ApplicationContext 的注册中心,可以注册一些非常基础的、在容器刷新前就必须使用的对象。
  • configureHeadlessProperty(): 设置 java.awt.headless 系统属性为 true。这在服务器环境下至关重要,它告诉 JVM 即使没有显示器、键盘等外设,也可以以“无头模式”运行,进行图像处理等操作。
  • SpringApplicationRunListeners: 这是一个包装器,内部持有一组 SpringApplicationRunListenergetRunListeners(args) 同样通过 spring.factories 加载它们(默认只有一个实现类:EventPublishingRunListener)。这些监听器是整个启动流程的“传感器”,它们会在不同的启动阶段调用相应的方法(如 startingenvironmentPreparedcontextPrepared 等),而这些方法的默认实现就是发布对应的 Spring 事件(如 ApplicationStartingEvent)。
    • listeners.starting(): 触发 ApplicationStartingEvent,标志着应用启动的最早期。

2.2 环境准备 (Environment Preparation)

Environment 是 Spring 应用中的核心抽象,它代表了应用运行时的环境,包含了无数的配置属性。

  • ApplicationArguments: 对传入的 main 方法参数 args 进行封装,方便后续使用。
  • prepareEnvironment(): 这是一个复杂的过程,我们深入进去看看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 1. 创建或获取 Environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 2. 配置 Environment,主要是添加命令行参数等
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 3. 附加配置属性源
ConfigurationPropertySources.attach(environment);
// 4. 发布环境已准备事件
listeners.environmentPrepared(bootstrapContext, environment);
// 5. 将 Environment 绑定到 SpringApplication
bindToSpringApplication(environment);
// 6. 根据情况转换 Environment 类型
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
// 7. 再次附加,确保最新
ConfigurationPropertySources.attach(environment);
return environment;
}
  • getOrCreateEnvironment(): 根据之前推断的 webApplicationType 创建对应类型的 Environment。例如,Servlet 环境会创建 StandardServletEnvironment
  • configureEnvironment(): 将命令行参数包装成 SimpleCommandLinePropertySource 并添加到 EnvironmentPropertySource 链中。
  • ConfigurationPropertySources.attach(): 这是一个巧妙的设计,它将 Environment 中的 PropertySource 适配为 Spring Boot 的 ConfigurationPropertySource,以便使用 @ConfigurationProperties 进行类型安全的属性绑定。
  • listeners.environmentPrepared(): 发布 ApplicationEnvironmentPreparedEvent。这是极其重要的一步,所有外部化配置(如 application.propertiesapplication.yml、OS 环境变量等)都是在这个事件被触发后,通过 PropertySourceLoader 解析并添加到 Environment 中的。监听这个事件的 ConfigFileApplicationListener(或新版本的 EnvironmentPostProcessor)完成了这一壮举。

prepareEnvironment 返回后,configureIgnoreBeanInfo(environment) 会被调用,设置 spring.beaninfo.ignore 属性,优化 JavaBeans Introspector 的缓存。

2.3 Banner 打印

printBanner(environment) 负责在控制台打印那个我们熟悉的 ASCII 艺术字。它可以从类路径、文件系统或通过 ResourceLoader 自定义 Banner 的内容。打印完后,Banner 对象会被保存下来,后续可能会注册到容器中。

2.4 应用上下文的创建与准备

  • createApplicationContext(): 又是一个基于 webApplicationType 的策略模式。对于 Servlet 环境,它会创建 AnnotationConfigServletWebServerApplicationContext。这个类继承了 ServletWebServerApplicationContext,而后者是 Spring Boot 嵌入式 Web 容器的关键所在。
  • prepareContext(): 这是启动过程中一个非常关键的“粘合剂”步骤,将前面准备好的各种组件(Environment、Banner、Listeners 等)与刚创建的 ApplicationContext 关联起来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 1. 设置 Environment
context.setEnvironment(environment);
// 2. 对 ApplicationContext 进行后置处理
postProcessApplicationContext(context);
// 3. 执行所有的 ApplicationContextInitializer
applyInitializers(context);
// 4. 发布上下文已初始化事件
listeners.contextPrepared(context);
// 5. 关闭引导上下文
bootstrapContext.close(context);
// 6. 打印启动日志和 Profile 信息
// ... (日志逻辑)
// 7. 将主配置类等注册为 Bean
load(context, sources.toArray(new Object[0]));
// 8. 发布上下文已加载事件
listeners.contextLoaded(context);
}
  • postProcessApplicationContext(): 设置 BeanFactory 的类加载器,注册 ConversionService 等默认组件。
  • applyInitializers(): 遍历并调用在构造阶段加载的所有 ApplicationContextInitializerinitialize 方法,对上下文进行最后的定制。
  • listeners.contextPrepared(): 发布 ApplicationContextInitializedEvent,此时上下文已创建,初始化器也已执行完毕,但尚未加载 Bean 定义。
  • bootstrapContext.close(context): 将引导上下文中注册的实例迁移到主 ApplicationContext 中,然后关闭引导上下文。
  • load(): 这是非常重要的一步,它将我们的主配置类(primarySources)通过 BeanDefinitionLoader 解析成 BeanDefinition,并注册到 ApplicationContextBeanFactory 中。正是这一步,让我们的 @SpringBootApplication 注解类成为一个配置类,为后续的自动配置和组件扫描奠定了基础。
  • listeners.contextLoaded(): 发布 ApplicationPreparedEvent,标志着 Bean 定义已加载完毕,容器已经准备好进行最终的初始化了。

2.5 刷新上下文 - refreshContext()

refreshContext(context) 是整个启动流程的高潮部分。它直接调用了 AbstractApplicationContext.refresh() 方法,这是 Spring 框架的“圣杯”方法,承载着 IoC 容器初始化的所有核心逻辑。Spring Boot 通过其创建的 ApplicationContext 实现类,巧妙地在这个原生流程中插入了启动嵌入式 Web 服务器的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// SpringApplication.java
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// ...
}
}
}

protected void refresh(ApplicationContext applicationContext) {
((AbstractApplicationContext) applicationContext).refresh();
}

我们聚焦于 refresh() 方法的几个关键步骤:

  1. prepareRefresh(): 刷新前的准备工作。
  2. obtainFreshBeanFactory(): 获取或创建新的 BeanFactory
  3. prepareBeanFactory(): 配置 BeanFactory 的标准特性。
  4. postProcessBeanFactory(): 这是模板方法,留给子类覆写。
  5. invokeBeanFactoryPostProcessors(): 执行 BeanFactoryPostProcessor,其中就包括我们熟知的 ConfigurationClassPostProcessor,它会解析 @Configuration 类,处理 @ComponentScan@Import 等注解,完成自动配置的加载和 BeanDefinition 的注册
  6. registerBeanPostProcessors(): 注册 BeanPostProcessor
  7. initMessageSource(): 初始化消息源。
  8. initApplicationEventMulticaster(): 初始化事件广播器。
  9. onRefresh(): 又一个模板方法,这里是 Spring Boot 启动嵌入式 Web 服务器的关键时刻! ServletWebServerApplicationContext 覆写了这个方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ServletWebServerApplicationContext.java
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}

private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
// ...
}

onRefresh() 阶段,ServletWebServerApplicationContext 会从 BeanFactory 中获取 ServletWebServerFactory(如 TomcatServletWebServerFactory),然后调用其 getWebServer 方法,创建并启动 Tomcat/Jetty/Undertow 等嵌入式 Web 服务器

  1. finishBeanFactoryInitialization(): 实例化所有剩余的单例(非延迟初始化)Bean。
  2. finishRefresh(): 刷新完成,发布 ContextRefreshedEvent

至此,一个功能完备的 Spring 容器已经启动完毕,内嵌的 Web 服务器也已开始监听端口。

2.6 启动后动作

  • afterRefresh(): 一个空的模板方法,留给开发者或子类在容器刷新完成后做自定义扩展。
  • listeners.started(): 发布 ApplicationStartedEvent,表明 Spring 容器已经启动,但尚未调用 ApplicationRunnerCommandLineRunner
  • callRunners(): 查找容器中所有实现了 ApplicationRunnerCommandLineRunner 接口的 Bean,并按 @Order 排序后依次执行它们的 run 方法。这是留给开发者进行启动后数据初始化、预加载等操作的绝佳位置。
  • listeners.running(): 发布 ApplicationReadyEvent,标志着应用已经成功启动并准备就绪,可以接收请求了。

2.7 异常处理

如果在启动的任何环节抛出异常,handleRunFailure 方法会被调用。它会尝试发布 ApplicationFailedEvent,并执行异常报告器(SpringBootExceptionReporter)来输出有意义的错误信息,帮助开发者快速定位问题。

3、Spring Boot内嵌Tomcat

3.1 触发点:onRefresh()createWebServer()

在 Spring Boot 的启动流程中,refreshContext(context) 最终调用了 AbstractApplicationContext.refresh()。其中,一个关键步骤是调用模板方法 onRefresh(),而 ServletWebServerApplicationContext(它是 Spring Boot 为 Servlet 环境提供的 ApplicationContext 实现)重写了该方法:

1
2
3
4
5
6
7
8
9
10
11
// ServletWebServerApplicationContext.java
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}

也就是说,当 IoC 容器刷新进入 onRefresh 阶段时,就会触发内嵌 Web 服务器的创建createWebServer() 的核心逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
// 1. 从容器中获取 ServletWebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
// 2. 使用工厂创建 WebServer,并传入一个 ServletContext 初始化器
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
// 如果已有 ServletContext(例如在传统 WAR 部署中),则直接包装
// ...
}
// 后续会调用 webServer.start() 启动服务器(在 finishRefresh 中)
}

这里有两个关键点:获取工厂使用工厂创建 WebServer

3.2 获取 ServletWebServerFactory

getWebServerFactory() 会从当前的 BeanFactory 中查找类型为 ServletWebServerFactory 的 Bean。由于我们引入了 spring-boot-starter-web,自动配置类 ServletWebServerFactoryAutoConfiguration 会生效,它根据类路径上的情况,向容器中注册对应的工厂 Bean。例如,如果存在 Tomcat 相关的类(Tomcat 本身),则会注册 TomcatServletWebServerFactory。Spring Boot 的自动配置逻辑如下(简化):

1
2
3
4
5
6
7
8
9
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}

所以,通常情况下我们得到的就是 TomcatServletWebServerFactory 实例。

3.3 创建内嵌 Tomcat:factory.getWebServer(initializer)

接下来进入 TomcatServletWebServerFactory.getWebServer(ServletContextInitializer... initializers) 方法。这个方法会完成 Tomcat 服务器的实例化、配置和启动准备。

3.3.1 构建 Tomcat 实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
// 1. 创建一个新的 Tomcat 实例
Tomcat tomcat = new Tomcat();
// 2. 设置工作目录(用于存放解压的 JSP 等临时文件)
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
// 3. 创建并配置 Connector(默认 HTTP/1.1)
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
// 4. 自定义连接器端口(可从配置文件中读取 server.port)
customizeConnector(connector);
tomcat.setConnector(connector);
// 5. 注册生命周期监听,以便优雅关闭
tomcat.getHost().setAutoDeploy(false);
// 6. 配置 Engine 的管道(pipeline),用于记录访问日志等
configureEngine(tomcat.getEngine());
// 7. 准备一个 TomcatEmbeddedContext(这是 Tomcat 的 Context 实现)
prepareContext(tomcat.getHost(), initializers);
// 8. 返回一个 TomcatWebServer 包装器,此时 Tomcat 尚未真正启动
return getTomcatWebServer(tomcat);
}

3.3.2 准备 TomcatEmbeddedContext

prepareContext() 方法负责在 Tomcat 的 Host 下添加一个 Context(即 Web 应用上下文),并且将我们传入的 ServletContextInitializer(即 getSelfInitializer() 返回的那个)与 Tomcat 的启动机制关联起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
// 1. 创建一个 TomcatEmbeddedContext
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
// 2. 设置 Context 的基础属性:路径、文档根目录、是否启用JSP等
context.setName(getContextPath());
context.setPath(getContextPath());
context.setDocBase(getDocBase());
context.addLifecycleListener(new FixContextListener());
// 3. 将用户自定义的 initializers 包装成一个 TomcatStarter
// TomcatStarter 实现了 javax.servlet.ServletContainerInitializer
TomcatStarter tomcatStarter = new TomcatStarter(initializers);
// 4. 将该 Starter 添加到 Context 的 initializers 中
context.addServletContainerInitializer(tomcatStarter, NO_CLASSES);
// 5. 将 Context 添加到 Host
host.addChild(context);
// 6. 配置其他组件,如 JarScanner、类加载器等
configureContext(context);
}

这里最重要的就是 **TomcatStarter**。它负责在 Tomcat 启动时,回调我们传入的 ServletContextInitializer,从而将 Spring 的 DispatcherServlet 等组件注册到原生的 ServletContext 中。

3.4 ServletContextInitializer 的作用:注册 DispatcherServlet

回到 ServletWebServerApplicationContext.getSelfInitializer() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}

private void selfInitialize(ServletContext servletContext) throws ServletException {
// 将当前的 ApplicationContext 作为属性存入 ServletContext
prepareWebApplicationContext(servletContext);
// 注册 Spring 的过滤器 DelegatingFilterProxy(如果有)
registerApplicationScope(servletContext);
// 关键:通过 ServletRegistrationBean 或直接向 ServletContext 添加 Servlet
// 这里实际上会触发 Spring Boot 自动配置中注册 DispatcherServlet 的逻辑
// 最终会调用 DispatcherServletAutoConfiguration 中配置的 DispatcherServletRegistrationBean
// 将 DispatcherServlet 注册到 ServletContext 中,映射到 "/"
this.servletContextInitializers.forEach(initializer -> initializer.onStartup(servletContext));
}

selfInitialize 方法实际上会被封装为 ServletContextInitializer,然后被 TomcatStarter 在 Tomcat 启动时调用。当 TomcatStarter.onStartup() 被执行时,它会遍历所有 ServletContextInitializer(包括我们传入的这个),调用它们的 onStartup 方法,完成对 ServletContext 的编程式配置。这正是 Spring MVC 的 DispatcherServlet 被注册到内嵌 Tomcat 的时机。

3.5 启动内嵌 Tomcat:TomcatWebServer.start()

factory.getWebServer(...) 返回的是一个 TomcatWebServer 对象,它包装了前面创建的 Tomcat 实例。此时 Tomcat 还没有真正启动(没有绑定端口、没有接受请求)。Tomcat 的启动时机在 finishRefresh() 阶段:

1
2
3
4
5
6
7
8
9
10
// ServletWebServerApplicationContext.java
@Override
protected void finishRefresh() {
super.finishRefresh();
// 启动 WebServer
WebServer webServer = startWebServer();
if (webServer != null) {
publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
}

startWebServer() 最终调用 TomcatWebServer.start()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void start() throws WebServerException {
synchronized (this.monitor) {
if (this.started) {
return;
}
try {
// 1. 启动 Tomcat 实例(调用 Tomcat 的 start 方法)
this.tomcat.start();
// 2. 重新抛出可能存在的异常(例如端口被占用)
rethrowDeferredStartupExceptions();
// 3. 启动一个后台线程,等待 Connector 真正进入运行状态
addPreviouslyRemovedConnectors();
// 4. 设置 started 标志,表示已经启动
this.started = true;
// 5. 输出日志(如 Tomcat started on port(s): 8080)
logger.info("Tomcat started on port(s): " + getPortsDescription() + " with context path '" + getContextPath() + "'");
}
catch (Exception ex) {
stopSilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}

tomcat.start() 会触发一系列组件的启动,包括 Connector(开始监听端口)、EngineHostContext 等。在启动 Context 时,TomcatStarter.onStartup() 被调用,进而执行 Spring 的 ServletContextInitializer,注册 DispatcherServlet 等组件。至此,内嵌 Tomcat 完全启动,可以处理 HTTP 请求。

3.6 优雅停止与 Shutdown Hook

Spring Boot 还注册了关闭钩子,确保在 JVM 退出时能优雅地停止内嵌 Tomcat:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// TomcatWebServer.java
public void stop() throws WebServerException {
synchronized (this.monitor) {
if (!this.started) {
return;
}
try {
this.tomcat.stop();
this.tomcat.destroy();
}
catch (Exception ex) {
throw new WebServerException("Unable to stop embedded Tomcat", ex);
}
finally {
this.started = false;
}
}
}

并且通过 AbstractApplicationContext.registerShutdownHook() 在容器关闭时调用 stop() 方法,从而释放端口、清理资源。


SpringBoot应用启动流程解析
https://johnjoyjzw.github.io/2023/01/21/SpringBoot应用启动流程解析/
Author
JiangZW
Posted on
January 21, 2023
Licensed under