SpringBoot内置tomcat启动过程及原理

springboot,内置,tomcat,启动,过程,原理 · 浏览次数 : 146

小编点评

**1. Spring Boot 中的 Tomcat 启动过程及原理** **2.1 下载 Tomcat 并实例化 Tomcat 工作目录** ```java Tomcat tomcat = new Tomcat(); ``` **2.2 设置 Tomcat 的工作目录** ```java tomcat.setBaseDir(baseDir.getAbsolutePath()); ``` **2.3 配置连接器** ```java Connector connector = new Connector(this.protocol); ``` **2.4 设置不自动部署** ```java tomcat.getHost().setAutoDeploy(false); ``` **2.5 创建 Tomcat 的 StandardContext** ```java prepareContext(tomcat.getHost(), initializers); ``` **2.6 创建 Tomcat 的 WebServer** ```java WebServer webServer = factory.getWebServer(getSelfInitializer()); ``` **2.7 配置 Tomcat 容器引擎** ```java configureEngine(tomcat.getEngine()); ``` **2.8 启动 Tomcat 服务** ```java // Start the server to trigger initialization listeners tomcat.start(); ``` **3. Spring Boot 中的 Tomcat 相关名称介绍** * Tomcat:Tomcat 是 Java 平台上一个用于开发服务器的 Java 应用程序框架。 * WebApplicationContext:Spring Boot 中使用 WebView 进行 Web 应用程序开发时创建的上下文。 * TomcatServletWebServerFactory:负责创建 Tomcat 的 Web 服务。 * Tomcat:Tomcat 是一个 Java 应用程序框架中的核心组件,负责处理 HTTP 请求和响应。

正文

作者:李岩科

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 的启动方法。

3.1 Tomcat 相关名称介绍

与SpringBoot内置tomcat启动过程及原理相似的内容:

SpringBoot内置tomcat启动过程及原理

作者:李岩科 1 背景 SpringBoot 是一个框架,一种全新的编程规范,他的产生简化了框架的使用,同时也提供了很多便捷的功能,比如内置 tomcat 就是其中一项,他让我们省去了搭建 tomcat 容器,生成 war,部署,启动 tomcat。因为内置了启动容器,应用程序可以直接通过 Mave

[转帖]Spring Boot中Tomcat是怎么启动的

https://zhuanlan.zhihu.com/p/208318177 Spring Boot一个非常突出的优点就是不需要我们额外再部署Servlet容器,它内置了多种容器的支持。我们可以通过配置来指定我们需要的容器。 本文以我们平时最常使用的容器Tomcat为列来介绍以下两个知识点: Spr

Springboot项目使用Undertow替换内置Tomcat服务器,实现RESTFUL接口web应用

Maven实例:pom.xml文件中添加更换依赖 org.springframework.boot spring-boot-starter

Springboot tomcat.threads线程数学习

Springboot tomcat.threads线程数学习 摘要 压测完nginx 突然想搞一下springboot内嵌的tomcat的 threads的参数 一不做二不休, 直接就着脚本进行学习和验证 修改参数 产品里面默认参数值是 500 我这边改大一下 tomcat threads: max

SpringBoot 集成 SpringSecurity + MySQL + JWT 附源码,废话不多直接盘

SpringBoot 集成 SpringSecurity + MySQL + JWT 无太多理论,直接盘 一般用于Web管理系统 可以先看 SpringBoot SpringSecurity 基于内存的使用介绍 本文介绍如何整合 SpringSecurity + MySQL + JWT 数据结构 数

SpringBoot SpringSecurity 介绍(基于内存的验证)

SpringBoot 集成 SpringSecurity + MySQL + JWT 附源码,废话不多直接盘 SpringBoot已经为用户采用默认配置,只需要引入pom依赖就能快速启动Spring Security。 目的:验证请求用户的身份,提供安全访问 优势:基于Spring,配置方便,减少大

[转帖]springboot连接redis cluster(带密码)

https://www.cnblogs.com/fengzi7314/p/15427669.html RedisConfig配置内容如下: package com.example.demo5.config; import org.springframework.beans.factory.annot

SpringBoot 整合 EasyExcel 实现自由导入导出,太强了

在实际的业务系统开发过程中,操作 Excel 实现数据的导入导出基本上是个非常常见的需求。 之前,我们有介绍一款非常好用的工具:EasyPoi,有读者提出在数据量大的情况下,EasyPoi 会占用内存大,性能不够好,严重的时候,还会出现内存异常的现象。 今天我给大家推荐一款性能更好的 Excel 导

[转帖]Springboot配置https访问

https://www.cnblogs.com/feifuzeng/p/14709372.html 介绍 该篇博文主要介绍如何配置Springboot使其打包部署的服务必须通过HTTPS协议才可访问,仅供内部研究使用。 生成https证书 要使用https,首先需要ssl证书,获取SSL证书有两种方

windows下使用dockerdesktop进行部署

Docker部署springboot项目 环境准备 要在windows上使用docker需要确认系统的需求 需要启用虚拟化支持的CPU 启用适用于windows的Linux子系统功能 保证足够的内存 下载dockerdesktop 下载后会提示安装对应的环境 坑点 安装过程中需要安装wsl环境,会遇