深度解析SpringBoot内嵌Web容器

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 今天分享一个SpringBoot的内嵌Web容器,在SpringBoot还没有出现时,我们使用Java开发了Web项目,需要将其部署到Tomcat下面,需要配置很多xml文件,SpringBoot出现后,就从繁琐的xml文件中解脱出来了,SpringBoot将Web容器进行了内嵌,我们只需要将项目打成一个jar包,就可以运行了,大大省略了开发成本,那么SpringBoot是怎么实现的呢,我们今天就来详细介绍。

前言


今天分享一个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的实现,里面会涉及到两个重要的接口WebServerWebServerFactory



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中很多地方也是这样的风格。



这个接口重要的两个子接口,也是我们需要关注的两个子接口分别是ServletWebServerFactoryReactiveWebServerFactory,它们两个都定义了一个方法getWebServer


JettyUndertowTomcat三个都属于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规范。


今天的分享就到这里,感谢你的观看,我们下期见,如果文中有不对或者不合理的地方,希望得到你的指点,我们一起在学习中成长,一起在成长中学习。


目录
相关文章
|
19天前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
38 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
22天前
|
安全 持续交付 Docker
深入理解并实践容器化技术——Docker 深度解析
深入理解并实践容器化技术——Docker 深度解析
43 2
|
27天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
29天前
|
运维 持续交付 虚拟化
深入解析Docker容器化技术的核心原理
深入解析Docker容器化技术的核心原理
45 1
|
1月前
|
JSON JavaScript 前端开发
蓝桥杯web组赛题解析和杯赛技巧
本文作者是一位自学前端两年半的大一学生,在第十五届蓝桥杯Web组比赛中获得省一和国三。文章详细解析了比赛题纲,涵盖HTML、CSS、JavaScript、Echarts和Vue等技术要点,并分享了备赛技巧和比赛经验。作者强调了多写代码和解题思路的重要性,同时提供了省赛和国赛的具体流程及注意事项。希望对参赛者有所帮助。
75 3
|
1月前
|
安全 前端开发 Java
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第26天】Web安全是现代软件开发的重要领域,本文深入探讨了XSS和CSRF两种常见攻击的原理及防御策略。针对XSS,介绍了输入验证与转义、使用CSP、WAF、HTTP-only Cookie和代码审查等方法。对于CSRF,提出了启用CSRF保护、设置CSRF Token、使用HTTPS、二次验证和用户教育等措施。通过这些策略,开发者可以构建更安全的Web应用。
91 4
|
1月前
|
安全 Go PHP
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第27天】本文深入解析了Web安全中的XSS和CSRF攻击防御策略。针对XSS,介绍了输入验证与净化、内容安全策略(CSP)和HTTP头部安全配置;针对CSRF,提出了使用CSRF令牌、验证HTTP请求头、限制同源策略和双重提交Cookie等方法,帮助开发者有效保护网站和用户数据安全。
71 2
|
21天前
|
XML Java 网络架构
使用 Spring Boot 公开 SOAP Web 服务端点:详细指南
使用 Spring Boot 公开 SOAP Web 服务端点:详细指南
31 0
|
2月前
|
缓存 前端开发 JavaScript
前端的全栈之路Meteor篇(二):容器化开发环境下的meteor工程架构解析
本文详细介绍了使用Docker创建Meteor项目的准备工作与步骤,解析了容器化Meteor项目的目录结构,包括工程准备、环境配置、容器启动及项目架构分析。提供了最佳实践建议,适合初学者参考学习。项目代码已托管至GitCode,方便读者实践与交流。
|
2月前
|
存储 应用服务中间件 云计算
深入解析:云计算中的容器化技术——Docker实战指南
【10月更文挑战第14天】深入解析:云计算中的容器化技术——Docker实战指南
82 1

推荐镜像

更多