SpringBoot内置Tomcat如何实现?启动原理与源码解析

 更新时间:2026年07月17日 11:35:17   作者:自橙一派  
你是否好奇SpringBoot为何能直接运行jar包而无需部署WAR?本文深入解析SpringBoot内置Tomcat的实现原理,从启动入口到Tomcat初始化,一步步拆解源码流程,带你理解SpringApplication如何创建Web容器并调用tomcat.start(),让你彻底掌握SpringBoot启动机制

1 背景

SpringBoot 是一个框架,一种全新的编程规范,他的产生简化了框架的使用,同时也提供了很多便捷的功能,比如内置 tomcat 就是其中一项,他让我们省去了搭建 tomcat 容器,生成 war,部署,启动 tomcat。

因为内置了启动容器,应用程序可以直接通过 Maven 命令将项目编译成可执行的 jar 包,通过 java -jar 命令直接启动,不需要再像以前一样,打包成 War 包,然后部署在 Tomcat 中。那么内置 tomcat 是如何实现的呢

2 tomcat 启动过程及原理

2.1 下载一个 springboot 项目

在这里下载一个项目 https://start.spring.io/ 也可以在 idea 新建 SpringBoot-Web 工程.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

点击 pom.xml 会有 tomcat 依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.1.2.RELEASE</version>
<scope>compile</scope>
</dependency>

2.2 从启动入口开始一步步探索

点击进入 run 方法

public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}

//继续点击进入run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}

进入到这个 run 方法之后就可以看到,我们认识的一些初始化事件。主要的过程也是在这里完成的。

2.3 源码代码流程大致是这样

/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
/**1、配置系统属性*/
configureHeadlessProperty();
/**2.获取监听器*/
SpringApplicationRunListeners listeners = getRunListeners(args);
/**发布应用开始启动事件 */
listeners.starting();
try {
/** 3.初始化参数 */
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
/** 4.配置环境*/
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);

configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
/**5.创建应用上下文*/
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
/**6.预处理上下文*/
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);

/**6.刷新上下文*/
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
/** 8.发布应用已经启动事件 */
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}

try {
/** 9.发布应用已经启动完成的监听事件 */
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

代码中主要就是通过 switch 语句,根据 webApplicationType 的类型来创建不同的 ApplicationContext:

  • DEFAULT_SERVLET_WEB_CONTEXT_CLASS:Web 类型,实例化 AnnotationConfigServletWebServerApplicationContext
  • DEFAULT_REACTIVE_WEB_CONTEXT_CLASS:响应式 Web 类型,实例化 AnnotationConfigReactiveWebServerApplicationContext
  • DEFAULT_CONTEXT_CLASS:非 Web 类型,实例化 AnnotationConfigApplicationContext

2.4 创建完应用上下文之后,我们在看刷新上下文方法

一步步通过断点点击方法进去查看,我们看到很熟悉代码 spring 的相关代码。

@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//初始化前的准备工作,主要是一些系统属性、环境变量的校验,比如Spring启动需要某些环境变量,可以在这个地方进行设置和校验
prepareRefresh();

// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
//准备bean工厂 注册了部分类
prepareBeanFactory(beanFactory);

try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.
//注册bean工厂后置处理器,并解析java代码配置bean定义
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
//注册bean后置处理器,并不会执行后置处理器,在后面实例化的时候执行
registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.
initMessageSource();

// Initialize event multicaster for this context.
//初始化事件监听多路广播器
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
//待子类实现,springBoot在这里实现创建内置的tomact容器
onRefresh();

// Check for listener beans and register them.
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();
}

catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

// Destroy already created singletons to avoid dangling resources.
destroyBeans();

// Reset 'active' flag.
cancelRefresh(ex);

// Propagate exception to caller.
throw ex;
}

finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

2.5 onRefresh () 方法是调用其子类实现的

也就是 ServletWebServerApplicationContext

/** 得到Servlet工厂 **/
this.webServer = factory.getWebServer(getSelfInitializer());

其中 createWebServer () 方法是用来启动 web 服务的,但是还没有真正启动 Tomcat,只是通过 ServletWebServerFactory 创建了一个 WebServer,继续来看这个 ServletWebServerFactory:

this.webServer = factory.getWebServer (getSelfInitializer ());

这个方法可以看出 TomcatServletWebServerFactory 的实现。相关 Tomcat 的实现。

2.6 TomcatServletWebServerFactory 的 getWebServer () 方法

清晰的看到 new 出来了一个 Tomcat.

2.7 Tomcat 创建之后,继续分析 Tomcat 的相关设置和参数

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {

/** 1、创建Tomcat实例 **/
Tomcat tomcat = new Tomcat();
//创建Tomcat工作目录
File baseDir = (this.baseDirectory != null) ? this.baseDirectory
: createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
/** 2、给创建好的tomcat设置连接器connector **/
tomcat.setConnector(connector);
/** 3.设置不自动部署 **/
tomcat.getHost().setAutoDeploy(false);
/** 4.配置Tomcat容器引擎 **/
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
/**准备Tomcat的StandardContext,并添加到Tomcat中*/
prepareContext(tomcat.getHost(), initializers);
/** 将创建好的Tomcat包装成WebServer返回**/
return getTomcatWebServer(tomcat);
}

2.8 继续点击 getTomcatWebServer 方法

找到 initialize () 方法,可以看到 tomcat.start (); 启动 tomcat 服务方法。

// Start the server to trigger initialization listeners
//启动tomcat服务
this.tomcat.start();
//开启阻塞非守护进程
startDaemonAwaitThread();

//Tomcat.java

2.9 TomcatWebServer.java 控制台会打印这句话

Tomcat started on port(s): 8080 (http) with context path ‘’

3 总结

SpringBoot 的启动主要是通过实例化 SpringApplication 来启动的,启动过程主要做了如下几件事情:

配置系统属性、获取监听器,发布应用开始启动事件、初始化参数、配置环境、创建应用上下文、预处理上下文、刷新上下文、再次刷新上下文、发布应用已经启动事件、发布应用启动完成事件。而启动 Tomcat 是刷新上下文 这一步。

Spring Boot 创建 Tomcat 时,会先创建一个上下文,将 WebApplicationContext 传给 Tomcat;

启动 Web 容器,需要调用 getWebserver (),因为默认的 Web 环境就是 TomcatServletWebServerFactory,所以会创建 Tomcat 的 Webserver,这里会把根上下文作为参数给 TomcatServletWebServerFactory 的 getWebServer ();启动 Tomcat,调用 Tomcat 中 Host、Engine 的启动方法。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 详解如何有效地处理Java中的多线程

    详解如何有效地处理Java中的多线程

    在现代软件开发中,多线程编程已成为提高程序性能和响应速度的重要手段,Java提供了丰富的多线程支持,使得在Java中实现并发操作变得相对简单,本文将深入探讨Java多线程编程的基本概念、常见问题和最佳实践,需要的朋友可以参考下
    2024-06-06
  • MyBatis与MyBatis-Plus的区别详解

    MyBatis与MyBatis-Plus的区别详解

    本文主要介绍了MyBatis与MyBatis-Plus的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • Java中常用解析工具jackson及fastjson的使用

    Java中常用解析工具jackson及fastjson的使用

    今天给大家带来的是关于Java解析工具的相关知识,文章围绕着jackson及fastjson的使用展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • Java8中接口的新特性测试

    Java8中接口的新特性测试

    今天小编就为大家分享一篇关于Java8中接口的新特性测试,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • javax.persistence中@Column定义字段类型方式

    javax.persistence中@Column定义字段类型方式

    这篇文章主要介绍了javax.persistence中@Column定义字段类型方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • 微信支付之公众号支付(java实现)

    微信支付之公众号支付(java实现)

    这篇文章主要介绍了微信支付之公众号支付(java实现),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • SpringCloud之loadbalancer负载均衡组件实战详解

    SpringCloud之loadbalancer负载均衡组件实战详解

    LoadBalancer是Spring Cloud官方提供的负载均衡组件,可用于替代Ribbon,这篇文章主要介绍了SpringCloud之loadbalancer负载均衡组件,需要的朋友可以参考下
    2023-06-06
  • SpringBoot整合Mail发送邮件功能

    SpringBoot整合Mail发送邮件功能

    我们在网站上注册账号的时候一般需要获取验证码,而这个验证码一般发送在你的手机号上还有的是发送在你的邮箱中,注册,账号密码…都需要用到验证,今天就演示一下如何用SpringBoot整合Mail发送邮箱
    2021-11-11
  • java实现置换密码加密解密

    java实现置换密码加密解密

    这篇文章主要为大家详细介绍了java实现置换密码加密解密,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-03-03
  • 三种Java求最大值的方法

    三种Java求最大值的方法

    本篇文章给大家总结了在JAVA中求最大值的三种常用方法,以及代码做了分享,需要的朋友参考下。
    2018-02-02

最新评论