我们每天将为您更新无码免费_婷婷中文字幕视频在线_yy6080无码av午夜福利免费_91精品国产免费久久久久久无码_黄片在线免费播放

當(dāng)前位置: 首頁(yè) >觀察 > 正文

深度解析SpringBoot內(nèi)嵌Web容器|每日速遞

2023-06-26 18:08:56 來(lái)源:博客園

你好,我是劉牌!

前言

今天分享一個(gè)SpringBoot的內(nèi)嵌Web容器,在SpringBoot還沒有出現(xiàn)時(shí),我們使用Java開發(fā)了Web項(xiàng)目,需要將其部署到Tomcat下面,需要配置很多xml文件,SpringBoot出現(xiàn)后,就從繁瑣的xml文件中解脫出來(lái)了,SpringBoot將Web容器進(jìn)行了內(nèi)嵌,我們只需要將項(xiàng)目打成一個(gè)jar包,就可以運(yùn)行了,大大省略了開發(fā)成本,那么SpringBoot是怎么實(shí)現(xiàn)的呢,我們今天就來(lái)詳細(xì)介紹。


(資料圖片)

SpringBoot提供的內(nèi)嵌容器

SpringBoot提供了四種Web容器,分別為Tomcat,Jetty,Undertow,Netty。

Tomcat

Spring Boot 默認(rèn)使用 Tomcat 作為嵌入式 Web 容器。Tomcat 作為一個(gè)流行的 Web 容器,容易能夠理解、配置和管理??梢酝ㄟ^使用spring-boot-starter-web來(lái)啟用 Tomcat 容器。

Jetty

Jetty 同樣是一個(gè)流行的嵌入式 Web 容器,它的缺省配置相對(duì)精簡(jiǎn),從而有利快速啟動(dòng)??梢酝ㄟ^使用spring-boot-starter-jetty來(lái)啟用 Jetty 容器。

Undertow

Undertow 是一個(gè)由 JBoss 開發(fā)的輕量級(jí)的嵌入式 Web 服務(wù)器。它具有出色的性能和低資源占用率,是一個(gè)適合微服務(wù)實(shí)現(xiàn)的 Web 服務(wù)器??梢允褂胹pring-boot-starter-undertow來(lái)啟用 Undertow 容器。

Netty

Netty是一個(gè)高性能的網(wǎng)絡(luò)框架,需要引入spring-boot-starter-webflux和spring-boot-starter-reactor-netty來(lái)開啟Netty作為Web容器。

使用

因?yàn)镾pringBoot默認(rèn)的是Tomcat作為Web容器,如果我們需要使用使用其他Web容器,那么需要排除Tomcat容器,再引入其他容器,Tomcat容器位于spring-boot-starter-web模塊下,所以我們需要在maven的pom.xml中移除Tomcat,如下。

      org.springframework.boot      spring-boot-starter-web      3.0.2                              org.springframework.boot              spring-boot-starter-tomcat                

然后引入對(duì)應(yīng)的Web容器,比如引入U(xiǎn)ndertow

      org.springframework.boot      spring-boot-starter-undertow

然后可以在yml文件中配置相應(yīng)容器的參數(shù),如下配置undertow.

server:  port: 8080  undertow:    threads:      worker: 10      io: 10    direct-buffers: true

其他web容器可以根據(jù)實(shí)際情況配置,從ServerProperties配置文件中可以查看對(duì)應(yīng)的Web容器的相關(guān)配置。

源碼解析

下面從源碼進(jìn)行分析,我們先使用SpringBoot的默認(rèn)Web容器Tomcat進(jìn)行分析。

那么源碼應(yīng)該從哪里看起呢,對(duì)于SpringBoot這么龐大復(fù)雜的項(xiàng)目,首先,我們?cè)谑褂肧pringBoot的時(shí)候,需要在application.yml文件中配置相關(guān)信息,比如端口,如果不配置端口,默認(rèn)是8080,那么這個(gè)端口肯定是web容器的端口,如果是Tomcat,那么Tomcat就設(shè)置為這個(gè)端口,Undertow也是,依此類推。

那么這里就是一個(gè)入口,在SpringBoot中,我們要獲取yml文件中的配置信息,一般是通過@ConfigurationProperties注解,我們可以按住ctrl,然后鼠標(biāo)點(diǎn)擊這個(gè)port,就能跳到對(duì)應(yīng)的屬性類里面。

屬性類ServerProperties就是專門獲取yml文件中的配置,然后以供使用。

到了屬性類里面后,我們繼續(xù)ctrl,然后會(huì)彈出很多類,如下所示。

因?yàn)槲覀兪褂玫氖荰omcat,那么就選擇一個(gè)Tomcat相關(guān)的類,我們選擇TomcatWebServerFactoryCustomizer,這個(gè)類實(shí)現(xiàn)了接口WebServerFactoryCustomizer,并實(shí)現(xiàn)了方法customize。

customize的參數(shù)是ConfigurableTomcatWebServerFactory,它是一個(gè)接口,它還繼承了接口ConfigurableWebServerFactory,我們從ConfigurableWebServerFactory中看出里面有設(shè)置端口,地址等方法。

我們?cè)倩仡^看ConfigurableTomcatWebServerFactory,可以看出里面是一些Tomcat相關(guān)的方法。

然后繼續(xù)看ConfigurableUndertowWebServerFactory,可以看出里面是對(duì)Undertow的一些屬性設(shè)置的方法。

我們回到TomcatWebServerFactoryCustomizer類中,SpringBoot使用了它的PropertyMapper類對(duì)屬性進(jìn)行設(shè)置,我們可以看出它使用propertyMapper.from().to()語(yǔ)法,其實(shí)就是將ServerProperties中的屬性設(shè)置到ConfigurableTomcatWebServerFactory中,這個(gè)屬性設(shè)置是在Spring對(duì)Bean進(jìn)行初始化時(shí)候設(shè)置的,使用的是Spring的后置處理器來(lái)實(shí)現(xiàn)的,后面我們繼續(xù)說。

然后我們繼續(xù)看一下TomcatWebServerFactoryCustomizer,他有一個(gè)構(gòu)造函數(shù),參數(shù)是Environment和ServerProperties,那么就證明其他地方對(duì)其進(jìn)行了new操作。

我們也是用ctrl套路,點(diǎn)擊構(gòu)造函數(shù)后跳到了EmbeddedWebServerFactoryCustomizerAutoConfiguration自動(dòng)裝配類中,這個(gè)類中有四個(gè)靜態(tài)類,我們可以看出,他們的作用都是創(chuàng)建對(duì)應(yīng)的定制器Bean,其實(shí)就是將yml文件中的Web容器配置進(jìn)行裝配,以供后面使用。

上面說的這一堆其實(shí)就是SpringBoot的自動(dòng)裝配,其目的就是創(chuàng)建對(duì)應(yīng)的Customizer,因?yàn)槊總€(gè)Web容器的配置項(xiàng)不一樣,所以就需要不同的Customizer和Factory。

上面說了這么多,怎么感覺和源碼沒關(guān)系呢,沒錯(cuò),其實(shí)上面說的并不是核心源碼,那么怎么找到核心源碼呢?我們思考一下,既然上面是部分源碼,那么源碼肯定會(huì)執(zhí)行到這里。

查看調(diào)用鏈

我們?cè)谏厦娴?code>TomcatWebServerFactoryCustomizer類中的customize方法中打一個(gè)斷點(diǎn),然后debug,于是得到調(diào)用鏈如下。

我們可以看出會(huì)調(diào)用onRefresh()方法,因?yàn)?code>AbstractApplicationContext使用的是模板方法模式,具體的實(shí)現(xiàn)交給子類實(shí)現(xiàn),因?yàn)槭褂玫氖荰omcat,所以交給了ServletWebServerApplicationContext類來(lái)實(shí)現(xiàn),具體的子類里面有一個(gè)createWebServer()方法,它就是創(chuàng)建Web容器。

具體實(shí)現(xiàn)如下,如下是Tomcat的實(shí)現(xiàn),里面會(huì)涉及到兩個(gè)重要的接口WebServerWebServerFactory。

WebServer

WebServer是容器的頂層接口,具體實(shí)現(xiàn)交給具體的容器實(shí)現(xiàn)類,如Tomcat則使用TomcatWebServer,Undertow則使用UndertowWebServer,Jetty,Netty也是如此。

此接口提供了一些方法,start()啟動(dòng)Web服務(wù)器,stop()停止Web服務(wù)器,getPort()獲取服務(wù)器端口。

不過對(duì)于start()和stop(),它們只是接口抽象的規(guī)范,在具體的實(shí)現(xiàn)中,也并不是全部都按照這個(gè)標(biāo)準(zhǔn),start()方法上有備注Starts the web server. Calling this method on an already started server has no effect.,翻譯為:啟動(dòng)web服務(wù)器。在已啟動(dòng)的服務(wù)器上調(diào)用此方法無(wú)效。,比如Tomcat的就沒有在start()方法中啟動(dòng)服務(wù)器,具體我們等會(huì)會(huì)看。

WebServerFactory

WebServerFactory是一個(gè)接口,沒有定義任何方法,它就創(chuàng)建Web服務(wù)器的工廠的標(biāo)記接口,Spring中很多地方也是這樣的風(fēng)格。

這個(gè)接口重要的兩個(gè)子接口,也是我們需要關(guān)注的兩個(gè)子接口分別是ServletWebServerFactoryReactiveWebServerFactory,它們兩個(gè)都定義了一個(gè)方法getWebServer。

JettyUndertow,Tomcat三個(gè)都屬于Servlet容器,所以使用的是ServletWebServerFactory來(lái)創(chuàng)建Web容器。

Netty不是Servlet容器,所以使用的是ReactiveWebServerFactory來(lái)創(chuàng)建Web容器。

上面對(duì)這兩個(gè)接口進(jìn)行了介紹,基本上整個(gè)Web容器都是圍繞這兩個(gè)接口來(lái),我們下面繼續(xù)分析。

獲取WebServerFactory

首先我們要先獲取web服務(wù)的工廠類的Bean,才能創(chuàng)建Web容器,因?yàn)槲覀兪褂玫氖荰omcat,所以獲取到的工廠類是TomcatServletWebServerFactory,具體的獲取Bean的過程我們就沒有必要去一一說明,只要對(duì)Spring IOC稍微熟悉一點(diǎn)就能理解,我們主要說一下在后置處理器。

上面我們介紹了Tomcat容器的定制器Customizer,里面對(duì)Web容器的配置屬性進(jìn)行組裝,它就是發(fā)生在Bean的初始化前,用到的Bean后置處理器是WebServerFactoryCustomizerBeanPostProcessor。

Bean的后置處理器中,會(huì)調(diào)用對(duì)應(yīng)的定制器,Tomcat調(diào)用的就是TomcatWebServerFactoryCustomizer,其他的也一樣,其目的都是定制WebServerFactory。

經(jīng)過一系列處理后,就從IOC容器中獲取到了WebServerFactoryBean,然后再使用這個(gè)工廠去創(chuàng)建Web服務(wù)。

創(chuàng)建Web服務(wù)

獲取到WebServerFactory后,就可以創(chuàng)建Web容器,因?yàn)槭褂玫氖荰omcat,所以使用的是TomcatServletWebServerFactory,如下,我們就看到了Tomcat的身影。

最后啟動(dòng)Tomcat容器是在TomcatWebServer中,在TomcatWebServer的構(gòu)造函數(shù)中調(diào)用initialize(),在initialize()中我們看是this.tomcat.start(),Tomcat被啟動(dòng)了。

上面我們?cè)谡fWebServer接口的時(shí)候,說了啟動(dòng)start()方法,在Tomcat的實(shí)現(xiàn)中就沒有使用start()來(lái)啟動(dòng)容器,但是在Undertow中,就使用了start()方法來(lái)啟動(dòng)容器。

Undertow容器啟動(dòng)

上面我們介紹了Tomcat容器的創(chuàng)建,Undertow的流程和Tomcat基本上是一樣的,但是在啟動(dòng)的時(shí)候,Undertow是在start()方法中啟動(dòng),而start()方法需要在finishRefresh()這一步中執(zhí)行。

在finishRefresh()中,會(huì)調(diào)用生命周期處理器

最終會(huì)走到WebServerStartStopLifecycle這個(gè)生命周期,這里就會(huì)調(diào)用WebServer中的start()方法。

最終在UndertowWebServer中啟動(dòng)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中使用,并對(duì)源碼進(jìn)行解析,在源碼解析這里,我們并沒有進(jìn)行芝麻細(xì)節(jié)式解析,而是從大體上進(jìn)行解析,只有對(duì)大致結(jié)構(gòu)了解,才能更好地進(jìn)行深度學(xué)習(xí)。

SpringBoot內(nèi)嵌容器涉及的知識(shí)點(diǎn)還是比較多,需要對(duì)Spring和SpringBoot有一定的了解才能更好地學(xué)習(xí)它,本文基于SpringBoot3.0進(jìn)行解析,SpringBoot3.0中,Servlet也是遵循Jakata EE規(guī)范。

今天的分享就到這里,感謝你的觀看,我們下期見,如果文中有不對(duì)或者不合理的地方,希望得到你的指點(diǎn),我們一起在學(xué)習(xí)中成長(zhǎng),一起在成長(zhǎng)中學(xué)習(xí)。

標(biāo)簽:

返回頂部