深度解析SpringBoot內(nèi)嵌Web容器
SpringBoot提供的內(nèi)嵌容器
SpringBoot提供了四種Web容器,分別為Tomcat,Jetty,Undertow,Netty。
Tomcat
Spring Boot 默認(rèn)使用 Tomcat 作為嵌入式 Web 容器。Tomcat 作為一個流行的 Web 容器,容易能夠理解、配置和管理??梢酝ㄟ^使用spring-boot-starter-web來啟用 Tomcat 容器。
Jetty
Jetty 同樣是一個流行的嵌入式 Web 容器,它的缺省配置相對精簡,從而有利快速啟動。可以通過使用spring-boot-starter-jetty來啟用 Jetty 容器。
Undertow
Undertow 是一個由 JBoss 開發(fā)的輕量級的嵌入式 Web 服務(wù)器。它具有出色的性能和低資源占用率,是一個適合微服務(wù)實(shí)現(xiàn)的 Web 服務(wù)器??梢允褂胹pring-boot-starter-undertow來啟用 Undertow 容器。
Netty
Netty是一個高性能的網(wǎng)絡(luò)框架,需要引入spring-boot-starter-webflux和spring-boot-starter-reactor-netty來開啟Netty作為Web容器。
使用
因?yàn)镾pringBoot默認(rèn)的是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>
然后引入對應(yīng)的Web容器,比如引入Undertow
<dependency> ??????<groupId>org.springframework.boot</groupId> ??????<artifactId>spring-boot-starter-undertow</artifactId> </dependency>
然后可以在yml文件中配置相應(yīng)容器的參數(shù),如下配置undertow.
server: ??port:?8080 ??undertow: ????threads: ??????worker:?10 ??????io:?10 ????direct-buffers:?true
其他web容器可以根據(jù)實(shí)際情況配置,從ServerProperties配置文件中可以查看對應(yīng)的Web容器的相關(guān)配置。
源碼解析
下面從源碼進(jìn)行分析,我們先使用SpringBoot的默認(rèn)Web容器Tomcat進(jìn)行分析。
那么源碼應(yīng)該從哪里看起呢,對于SpringBoot這么龐大復(fù)雜的項(xiàng)目,首先,我們在使用SpringBoot的時候,需要在application.yml文件中配置相關(guān)信息,比如端口,如果不配置端口,默認(rèn)是8080,那么這個端口肯定是web容器的端口,如果是Tomcat,那么Tomcat就設(shè)置為這個端口,Undertow也是,依此類推。
那么這里就是一個入口,在SpringBoot中,我們要獲取yml文件中的配置信息,一般是通過@ConfigurationProperties
注解,我們可以按住ctrl,然后鼠標(biāo)點(diǎn)擊這個port,就能跳到對應(yīng)的屬性類里面。
屬性類ServerProperties就是專門獲取yml文件中的配置,然后以供使用。
到了屬性類里面后,我們繼續(xù)ctrl,然后會彈出很多類,如下所示。
因?yàn)槲覀兪褂玫氖荰omcat,那么就選擇一個Tomcat相關(guān)的類,我們選擇TomcatWebServerFactoryCustomizer
,這個類實(shí)現(xiàn)了接口WebServerFactoryCustomizer
,并實(shí)現(xiàn)了方法customize。
customize的參數(shù)是ConfigurableTomcatWebServerFactory
,它是一個接口,它還繼承了接口ConfigurableWebServerFactory
,我們從ConfigurableWebServerFactory
中看出里面有設(shè)置端口,地址等方法。
我們再回頭看ConfigurableTomcatWebServerFactory
,可以看出里面是一些Tomcat相關(guān)的方法。
然后繼續(xù)看ConfigurableUndertowWebServerFactory
,可以看出里面是對Undertow的一些屬性設(shè)置的方法。
我們回到TomcatWebServerFactoryCustomizer
類中,SpringBoot使用了它的PropertyMapper
類對屬性進(jìn)行設(shè)置,我們可以看出它使用propertyMapper.from().to()語法,其實(shí)就是將ServerProperties中的屬性設(shè)置到ConfigurableTomcatWebServerFactory中,這個屬性設(shè)置是在Spring對Bean進(jìn)行初始化時候設(shè)置的,使用的是Spring的后置處理器來實(shí)現(xiàn)的,后面我們繼續(xù)說。
然后我們繼續(xù)看一下TomcatWebServerFactoryCustomizer
,他有一個構(gòu)造函數(shù),參數(shù)是Environment和ServerProperties,那么就證明其他地方對其進(jìn)行了new操作。
我們也是用ctrl套路,點(diǎn)擊構(gòu)造函數(shù)后跳到了EmbeddedWebServerFactoryCustomizerAutoConfiguration
自動裝配類中,這個類中有四個靜態(tài)類,我們可以看出,他們的作用都是創(chuàng)建對應(yīng)的定制器Bean,其實(shí)就是將yml文件中的Web容器配置進(jìn)行裝配,以供后面使用。
上面說的這一堆其實(shí)就是SpringBoot的自動裝配,其目的就是創(chuàng)建對應(yīng)的Customizer,因?yàn)槊總€Web容器的配置項(xiàng)不一樣,所以就需要不同的Customizer和Factory。
上面說了這么多,怎么感覺和源碼沒關(guān)系呢,沒錯,其實(shí)上面說的并不是核心源碼,那么怎么找到核心源碼呢?我們思考一下,既然上面是部分源碼,那么源碼肯定會執(zhí)行到這里。
查看調(diào)用鏈
我們在上面的TomcatWebServerFactoryCustomizer
類中的customize
方法中打一個斷點(diǎn),然后debug,于是得到調(diào)用鏈如下。
我們可以看出會調(diào)用onRefresh()
方法,因?yàn)?code>AbstractApplicationContext使用的是模板方法模式,具體的實(shí)現(xiàn)交給子類實(shí)現(xiàn),因?yàn)槭褂玫氖荰omcat,所以交給了ServletWebServerApplicationContext
類來實(shí)現(xiàn),具體的子類里面有一個createWebServer()
方法,它就是創(chuàng)建Web容器。
具體實(shí)現(xiàn)如下,如下是Tomcat的實(shí)現(xiàn),里面會涉及到兩個重要的接口WebServer
和WebServerFactory
。
WebServer
WebServer是容器的頂層接口,具體實(shí)現(xiàn)交給具體的容器實(shí)現(xiàn)類,如Tomcat則使用TomcatWebServer
,Undertow則使用UndertowWebServer
,Jetty,Netty也是如此。
此接口提供了一些方法,start()啟動Web服務(wù)器,stop()停止Web服務(wù)器,getPort()獲取服務(wù)器端口。
不過對于start()和stop(),它們只是接口抽象的規(guī)范,在具體的實(shí)現(xiàn)中,也并不是全部都按照這個標(biāo)準(zhǔn),start()方法上有備注Starts the web server. Calling this method on an already started server has no effect.
,翻譯為:啟動web服務(wù)器。在已啟動的服務(wù)器上調(diào)用此方法無效。
,比如Tomcat的就沒有在start()方法中啟動服務(wù)器,具體我們等會會看。
WebServerFactory
WebServerFactory是一個接口,沒有定義任何方法,它就創(chuàng)建Web服務(wù)器的工廠的標(biāo)記接口,Spring中很多地方也是這樣的風(fēng)格。
這個接口重要的兩個子接口,也是我們需要關(guān)注的兩個子接口分別是ServletWebServerFactory
和ReactiveWebServerFactory
,它們兩個都定義了一個方法getWebServer
。
Jetty
,Undertow
,Tomcat
三個都屬于Servlet容器,所以使用的是ServletWebServerFactory
來創(chuàng)建Web容器。
而Netty
不是Servlet容器,所以使用的是ReactiveWebServerFactory
來創(chuàng)建Web容器。
上面對這兩個接口進(jìn)行了介紹,基本上整個Web容器都是圍繞這兩個接口來,我們下面繼續(xù)分析。
獲取WebServerFactory
首先我們要先獲取web服務(wù)的工廠類的Bean,才能創(chuàng)建Web容器,因?yàn)槲覀兪褂玫氖荰omcat,所以獲取到的工廠類是TomcatServletWebServerFactory
,具體的獲取Bean的過程我們就沒有必要去一一說明,只要對Spring IOC稍微熟悉一點(diǎn)就能理解,我們主要說一下在后置處理器。
上面我們介紹了Tomcat容器的定制器Customizer,里面對Web容器的配置屬性進(jìn)行組裝,它就是發(fā)生在Bean的初始化前,用到的Bean后置處理器是WebServerFactoryCustomizerBeanPostProcessor
。
Bean的后置處理器中,會調(diào)用對應(yīng)的定制器,Tomcat調(diào)用的就是TomcatWebServerFactoryCustomizer
,其他的也一樣,其目的都是定制WebServerFactory。
經(jīng)過一系列處理后,就從IOC容器中獲取到了WebServerFactory
Bean,然后再使用這個工廠去創(chuàng)建Web服務(wù)。
創(chuàng)建Web服務(wù)
獲取到WebServerFactory后,就可以創(chuàng)建Web容器,因?yàn)槭褂玫氖荰omcat,所以使用的是TomcatServletWebServerFactory
,如下,我們就看到了Tomcat的身影。
最后啟動Tomcat容器是在TomcatWebServer中,在TomcatWebServer的構(gòu)造函數(shù)中調(diào)用initialize(),在initialize()中我們看是this.tomcat.start(),Tomcat被啟動了。
上面我們在說WebServer
接口的時候,說了啟動start()
方法,在Tomcat的實(shí)現(xiàn)中就沒有使用start()
來啟動容器,但是在Undertow中,就使用了start()
方法來啟動容器。
Undertow容器啟動
上面我們介紹了Tomcat容器的創(chuàng)建,Undertow的流程和Tomcat基本上是一樣的,但是在啟動的時候,Undertow是在start()方法中啟動,而start()方法需要在 finishRefresh()這一步中執(zhí)行。
在finishRefresh()中,會調(diào)用生命周期處理器
最終會走到WebServerStartStopLifecycle這個生命周期,這里就會調(diào)用WebServer中的start()方法。
最終在UndertowWebServer中啟動Undertow容器
具體執(zhí)行順序如下。
finishRefresh() -> getLifecycleProcessor().onRefresh() -> startBeans(true) -> start() -> doStart(this.lifecycleBeans, member.name, this.autoStartupOnly) -> bean.start() -> this.webServer.start()
上面我們分析了Tomcat和Undertow的創(chuàng)建流程,Jetty和Netty也是大同小異,因?yàn)镾pring使用了模板方法模式,具體的實(shí)現(xiàn)交給具體的Web容器,所以在整體結(jié)構(gòu)上是差不多的,只是實(shí)現(xiàn)方式不同。
總結(jié)
關(guān)于SpringBoot的內(nèi)嵌Web容器,就說得差不多了,我們從各種Web容器進(jìn)行介紹,包括他們的有點(diǎn),怎么在SpringBoot中使用,并對源碼進(jìn)行解析,在源碼解析這里,我們并沒有進(jìn)行芝麻細(xì)節(jié)式解析,而是從大體上進(jìn)行解析,只有對大致結(jié)構(gòu)了解,才能更好地進(jìn)行深度學(xué)習(xí)。
SpringBoot內(nèi)嵌容器涉及的知識點(diǎn)還是比較多,需要對Spring和SpringBoot有一定的了解才能更好地學(xué)習(xí)它,本文基于SpringBoot3.0進(jìn)行解析, SpringBoot3.0中,Servlet也是遵循Jakata EE規(guī)范。
以上就是深度解析SpringBoot內(nèi)嵌Web容器的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot Web容器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring boot+mybatis+thymeleaf 實(shí)現(xiàn)登錄注冊增刪改查功能的示例代碼
這篇文章主要介紹了Spring boot+mybatis+thymeleaf 實(shí)現(xiàn)登錄注冊增刪改查功能的示例代碼,本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07SpringBoot事件監(jiān)聽器@EventListener的實(shí)現(xiàn)
@EventListener注解用于處理應(yīng)用程序事件,提供了一種方便的方式來監(jiān)聽和響應(yīng)各種事件,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-11-11Maven項(xiàng)目中讀取src/main/resources目錄下的配置文件的方法
本篇文章主要介紹了Maven項(xiàng)目中讀取src/main/resources目錄下的配置文件的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12Java的深拷貝與淺拷貝的幾種實(shí)現(xiàn)方式
這篇文章主要介紹了Java的深拷貝與淺拷貝的幾種實(shí)現(xiàn)方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01