深度解析SpringBoot内嵌Web容器
你好,我是刘牌!
前言
今天分享一个SpringBoot的内嵌Web容器,在SpringBoot还没有出现时,我们使用Java开发了Web项目,需要将其部署到Tomcat下面,需要配置很多xml文件,SpringBoot出现后,就从繁琐的xml文件中解脱出来了,SpringBoot将Web容器进行了内嵌,我们只需要将项目打成一个jar包,就可以运行了,大大省略了开发成本,那么SpringBoot是怎么实现的呢,我们今天就来详细介绍。
SpringBoot提供的内嵌容器
SpringBoot提供了四种Web容器,分别为Tomcat,Jetty,Undertow,Netty。
Tomcat
Spring Boot 默认使用 Tomcat 作为嵌入式 Web 容器。Tomcat 作为一个流行的 Web 容器,容易能够理解、配置和管理。可以通过使用spring-boot-starter-web来启用 Tomcat 容器。
Jetty
Jetty 同样是一个流行的嵌入式 Web 容器,它的缺省配置相对精简,从而有利快速启动。可以通过使用spring-boot-starter-jetty来启用 Jetty 容器。
Undertow
Undertow 是一个由 JBoss 开发的轻量级的嵌入式 Web 服务器。它具有出色的性能和低资源占用率,是一个适合微服务实现的 Web 服务器。可以使用spring-boot-starter-undertow来启用 Undertow 容器。
Netty
Netty是一个高性能的网络框架,需要引入spring-boot-starter-webflux和spring-boot-starter-reactor-netty来开启Netty作为Web容器。
使用
因为SpringBoot默认的是Tomcat作为Web容器,如果我们需要使用使用其他Web容器,那么需要排除Tomcat容器,再引入其他容器,Tomcat容器位于spring-boot-starter-web模块下,所以我们需要在maven的pom.xml中移除Tomcat,如下。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.0.2</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
然后引入对应的Web容器,比如引入Undertow
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
然后可以在yml文件中配置相应容器的参数,如下配置undertow.
server:
port: 8080
undertow:
threads:
worker: 10
io: 10
direct-buffers: true
其他web容器可以根据实际情况配置,从ServerProperties配置文件中可以查看对应的Web容器的相关配置。

源码解析
下面从源码进行分析,我们先使用SpringBoot的默认Web容器Tomcat进行分析。
那么源码应该从哪里看起呢,对于SpringBoot这么庞大复杂的项目,首先,我们在使用SpringBoot的时候,需要在application.yml文件中配置相关信息,比如端口,如果不配置端口,默认是8080,那么这个端口肯定是web容器的端口,如果是Tomcat,那么Tomcat就设置为这个端口,Undertow也是,依此类推。
那么这里就是一个入口,在SpringBoot中,我们要获取yml文件中的配置信息,一般是通过@ConfigurationProperties注解,我们可以按住ctrl,然后鼠标点击这个port,就能跳到对应的属性类里面。

属性类ServerProperties就是专门获取yml文件中的配置,然后以供使用。

到了属性类里面后,我们继续ctrl,然后会弹出很多类,如下所示。

因为我们使用的是Tomcat,那么就选择一个Tomcat相关的类,我们选择TomcatWebServerFactoryCustomizer,这个类实现了接口WebServerFactoryCustomizer,并实现了方法customize。

customize的参数是ConfigurableTomcatWebServerFactory,它是一个接口,它还继承了接口ConfigurableWebServerFactory,我们从ConfigurableWebServerFactory中看出里面有设置端口,地址等方法。

我们再回头看ConfigurableTomcatWebServerFactory,可以看出里面是一些Tomcat相关的方法。

然后继续看ConfigurableUndertowWebServerFactory,可以看出里面是对Undertow的一些属性设置的方法。

我们回到TomcatWebServerFactoryCustomizer类中,SpringBoot使用了它的PropertyMapper类对属性进行设置,我们可以看出它使用propertyMapper.from().to()语法,其实就是将ServerProperties中的属性设置到ConfigurableTomcatWebServerFactory中,这个属性设置是在Spring对Bean进行初始化时候设置的,使用的是Spring的后置处理器来实现的,后面我们继续说。

然后我们继续看一下TomcatWebServerFactoryCustomizer,他有一个构造函数,参数是Environment和ServerProperties,那么就证明其他地方对其进行了new操作。

我们也是用ctrl套路,点击构造函数后跳到了EmbeddedWebServerFactoryCustomizerAutoConfiguration自动装配类中,这个类中有四个静态类,我们可以看出,他们的作用都是创建对应的定制器Bean,其实就是将yml文件中的Web容器配置进行装配,以供后面使用。

上面说的这一堆其实就是SpringBoot的自动装配,其目的就是创建对应的Customizer,因为每个Web容器的配置项不一样,所以就需要不同的Customizer和Factory。
上面说了这么多,怎么感觉和源码没关系呢,没错,其实上面说的并不是核心源码,那么怎么找到核心源码呢?我们思考一下,既然上面是部分源码,那么源码肯定会执行到这里。
查看调用链
我们在上面的TomcatWebServerFactoryCustomizer类中的customize方法中打一个断点,然后debug,于是得到调用链如下。

我们可以看出会调用onRefresh()方法,因为AbstractApplicationContext使用的是模板方法模式,具体的实现交给子类实现,因为使用的是Tomcat,所以交给了ServletWebServerApplicationContext类来实现,具体的子类里面有一个createWebServer()方法,它就是创建Web容器。

具体实现如下,如下是Tomcat的实现,里面会涉及到两个重要的接口WebServer和WebServerFactory。

WebServer
WebServer是容器的顶层接口,具体实现交给具体的容器实现类,如Tomcat则使用TomcatWebServer,Undertow则使用UndertowWebServer,Jetty,Netty也是如此。
此接口提供了一些方法,start()启动Web服务器,stop()停止Web服务器,getPort()获取服务器端口。

不过对于start()和stop(),它们只是接口抽象的规范,在具体的实现中,也并不是全部都按照这个标准,start()方法上有备注Starts the web server. Calling this method on an already started server has no effect.,翻译为:启动web服务器。在已启动的服务器上调用此方法无效。,比如Tomcat的就没有在start()方法中启动服务器,具体我们等会会看。
WebServerFactory
WebServerFactory是一个接口,没有定义任何方法,它就创建Web服务器的工厂的标记接口,Spring中很多地方也是这样的风格。

这个接口重要的两个子接口,也是我们需要关注的两个子接口分别是ServletWebServerFactory和ReactiveWebServerFactory,它们两个都定义了一个方法getWebServer。
Jetty,Undertow,Tomcat三个都属于Servlet容器,所以使用的是ServletWebServerFactory来创建Web容器。

而Netty不是Servlet容器,所以使用的是ReactiveWebServerFactory来创建Web容器。

上面对这两个接口进行了介绍,基本上整个Web容器都是围绕这两个接口来,我们下面继续分析。
获取WebServerFactory
首先我们要先获取web服务的工厂类的Bean,才能创建Web容器,因为我们使用的是Tomcat,所以获取到的工厂类是TomcatServletWebServerFactory,具体的获取Bean的过程我们就没有必要去一一说明,只要对Spring IOC稍微熟悉一点就能理解,我们主要说一下在后置处理器。
上面我们介绍了Tomcat容器的定制器Customizer,里面对Web容器的配置属性进行组装,它就是发生在Bean的初始化前,用到的Bean后置处理器是WebServerFactoryCustomizerBeanPostProcessor。

Bean的后置处理器中,会调用对应的定制器,Tomcat调用的就是TomcatWebServerFactoryCustomizer,其他的也一样,其目的都是定制WebServerFactory。
经过一系列处理后,就从IOC容器中获取到了WebServerFactoryBean,然后再使用这个工厂去创建Web服务。
创建Web服务
获取到WebServerFactory后,就可以创建Web容器,因为使用的是Tomcat,所以使用的是TomcatServletWebServerFactory,如下,我们就看到了Tomcat的身影。

最后启动Tomcat容器是在TomcatWebServer中,在TomcatWebServer的构造函数中调用initialize(),在initialize()中我们看是this.tomcat.start(),Tomcat被启动了。

上面我们在说WebServer接口的时候,说了启动start()方法,在Tomcat的实现中就没有使用start()来启动容器,但是在Undertow中,就使用了start()方法来启动容器。
Undertow容器启动
上面我们介绍了Tomcat容器的创建,Undertow的流程和Tomcat基本上是一样的,但是在启动的时候,Undertow是在start()方法中启动,而start()方法需要在
finishRefresh()这一步中执行。

在finishRefresh()中,会调用生命周期处理器

最终会走到WebServerStartStopLifecycle这个生命周期,这里就会调用WebServer中的start()方法。

最终在UndertowWebServer中启动Undertow容器

具体执行顺序如下。
finishRefresh() -> getLifecycleProcessor().onRefresh() -> startBeans(true) -> start() -> doStart(this.lifecycleBeans, member.name, this.autoStartupOnly) -> bean.start() -> this.webServer.start()
上面我们分析了Tomcat和Undertow的创建流程,Jetty和Netty也是大同小异,因为Spring使用了模板方法模式,具体的实现交给具体的Web容器,所以在整体结构上是差不多的,只是实现方式不同。
总结
关于SpringBoot的内嵌Web容器,就说得差不多了,我们从各种Web容器进行介绍,包括他们的有点,怎么在SpringBoot中使用,并对源码进行解析,在源码解析这里,我们并没有进行芝麻细节式解析,而是从大体上进行解析,只有对大致结构了解,才能更好地进行深度学习。
SpringBoot内嵌容器涉及的知识点还是比较多,需要对Spring和SpringBoot有一定的了解才能更好地学习它,本文基于SpringBoot3.0进行解析,
SpringBoot3.0中,Servlet也是遵循Jakata EE规范。
今天的分享就到这里,感谢你的观看,我们下期见,如果文中有不对或者不合理的地方,希望得到你的指点,我们一起在学习中成长,一起在成长中学习。
深度解析SpringBoot内嵌Web容器的更多相关文章
- spring-boot内嵌三大容器https设置
spring-boot内嵌三大容器https设置 spring-boot默认的内嵌容器为tomcat,除了tomcat之前还可以设置jetty和undertow. 1.设置https spring-b ...
- maven打包排除spring-boot内嵌tomcat容器依赖jar
在pom文件中添加打包排除配置信息. <plugin> <artifactId>maven-war-plugin</artifactId> <version& ...
- [Web Server]Tomcat调优之SpringBoot内嵌Tomcat源码分析
以springboot:2.3.12.RELEASE中内嵌的tomcat-embed-core:9.0.46为例,进行分析 1 概述 1.0 关键依赖包 spring-boot-autoconfigu ...
- SpringBoot内嵌Tomcat开启APR模式(运行环境为Centos7)
网上查到的一些springboot内嵌的tomcat开启apr的文章,好像使用的springboot版本较老,在SpringBoot 2.0.4.RELEASE中已经行不通了.自己整理了一下,供参考. ...
- 精尽Spring Boot源码分析 - 内嵌Tomcat容器的实现
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
- 查看和指定SpringBoot内嵌Tomcat的版本
查看当前使用的Tomcat版本号 Maven Repository中查看 比如我们需要查Spring Boot 2.1.4-RELEASE的内嵌Tomcat版本, 可以打开链接: https://mv ...
- spring内嵌jetty容器,实现main方法启动web项目
Jetty 是一个开源的servlet容器,它为基于Java的web容器,例如JSP和servlet提供运行环境.Jetty是使用Java语言编写的,它的API以一组JAR包的形式发布.开发人员可以将 ...
- SpringBoot 内嵌容器的比较
Spring Boot内嵌容器支持Tomcat.Jetty.Undertow.为什么选择Undertow? 这里有一篇文章,时间 2017年1月26日发布的: 参考 Tomcat vs. Jetty ...
- SpringBoot源码篇:深度分析SpringBoot如何省去web.xml
一.前言 从本博文开始,正式开启Spring及SpringBoot源码分析之旅.这可能是一个漫长的过程,因为本人之前阅读源码都是很片面的,对Spring源码没有一个系统的认识.从本文开始我会持续更新, ...
- SpringBoot内嵌数据库的使用(H2)
配置数据源(DataSource) Java的javax.sql.DataSource接口提供了一个标准的使用数据库连接的方法. 传统做法是, 一个DataSource使用一个URL以及相应的证书去构 ...
随机推荐
- 【AIGC未来的发展方向】面向人工智能的第一步,一文告诉你人工智能是什么以及未来的方向分析
人工智能的概念 当人们提到"人工智能(AI)"时,很多人会想到机器人和未来世界的科幻场景,但AI的应用远远不止于此.现在,AI已经广泛应用于各种行业和生活领域,为我们带来了无限可能 ...
- 阿里版ChatGPT:通义千问pk文心一言
随着 ChatGPT 热潮卷起来,百度发布了文心一言.Google 发布了 Bard,「阿里云」官方终于也宣布了,旗下的 AI 大模型"通义千问"正式开启测试! 申请地址:http ...
- 19.13备库duplicate恢复新主库(二)
问题描述:主备两个库不在同一个机房,此时想从这一套库中在复制一套可读可写的新库出来.网络带宽要求比较高,需要从备库中使用备份在起一个新库,也要测试下使用duplicate从备库能够在复制一个新库.经过 ...
- .NET周报 【4月第3期 2023-04-15】
国内文章 Semantic Kernel 入门系列: Planner 规划器 https://www.cnblogs.com/xbotter/p/semantic_kernel_introductio ...
- 迁移学习(COAL)《Generalized Domain Adaptation with Covariate and Label Shift CO-ALignment》
论文信息 论文标题:Generalized Domain Adaptation with Covariate and Label Shift CO-ALignment论文作者:Shuhan Tan, ...
- java处理集合工具
public static <K, V> Map<K, V> parseListToMap(Collection<V> list, Function<V, K ...
- 1分钟了解C语言正确使用字节对齐及#pragma pack的方法
C/C++编译器的缺省字节对齐方式为自然对界.即在缺省情况下,编译器为每一个变量或是数据单元按其自然对界条件分配空间. 在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空 ...
- Extjs4 Tree Grid 综合示例(展开、编辑列、获取数据)
用json数据模拟后端传回来的结果,Extjs tree支持两种类型的结构,一种是带children属性的嵌套式的数据,一种是扁平的,每条记录带pid的数据,带pid的添加配置项可以自动解析成树形结构 ...
- 消息推送平台的实时数仓?!flink消费kafka消息入到hive
大家好,3y啊.好些天没更新了,并没有偷懒,只不过一直在安装环境,差点都想放弃了. 上一次比较大的更新是做了austin的预览地址,把企业微信的应用和机器人消息各种的消息类型和功能给完善了.上一篇文章 ...
- [MAUI程序设计]界面多态与实现
目录 需求一:在不同设备上使用不同 UI 外观 定义控件 UI 外观 基于平台自定义配置 需求二:在不同数据类别中使用不同的 UI 外观 定义视图 UI 外观 创建数据模板 创建选择器 定义数据 需求 ...