SpringBoot?的?web?類型推斷詳解
用了這么多年的 SpringBoot 那么你知道什么是 SpringBoot 的 web 類型推斷嗎?
估計(jì)很多小伙伴都不知道,畢竟平時(shí)開發(fā)做項(xiàng)目的時(shí)候做的都是普通的 web 項(xiàng)目并不需要什么特別的了解,不過抱著學(xué)習(xí)的心態(tài),阿粉今天帶大家看一下什么是 SpringBoot 的 web 類型推斷。
SpringBoot 的 web 類型有哪些
既然是web 類型推斷,那我們肯定要知道 SpringBoot 支持哪些類型,然后才能分析是怎樣進(jìn)行類型推斷的。
根據(jù)官方的介紹 SpringBoot 的 web 類型有三種,分別是,NONE、SERVLET 和 REACTIVE,定義在枚舉 WebApplicationType 中,這三種類型分別代表了三種含義:
NONE:不是一個(gè) web 應(yīng)用,不需要啟動內(nèi)置的 web 服務(wù)器; SERVLET:基于 servlet 的 web 應(yīng)用,需要啟動一個(gè)內(nèi)置的 servlet 服務(wù)器; REACTIVE:一個(gè) reactive 的 web 應(yīng)用,需要啟動一個(gè)內(nèi)置的 reactive 服務(wù)器;
public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
}
web 類型推斷
上面提到了 SpringBoot 的三種 web 類型,接下來我們先通過代碼驗(yàn)證一下,然后再分析一下 SpringBoot 是如何進(jìn)行類型推斷的。
首先我們通過在 https://start.spring.io/ 快速的構(gòu)建三種類型的項(xiàng)目,三種類型的項(xiàng)目配置除了依賴不一樣之外,其他都一樣,如下所示
None web

下載后的項(xiàng)目文件 pom 中對應(yīng)的依賴為
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
Servlet web

下載后的項(xiàng)目文件 pom 中對應(yīng)的依賴為
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Reactive web

下載后的項(xiàng)目文件 pom 中對應(yīng)的依賴為
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
接下來我們依次啟動三個(gè)項(xiàng)目看看有什么區(qū)別,
啟動 None web

通過啟動日志我們可以看到,在 None web 類型下,應(yīng)用啟動運(yùn)行后就自動關(guān)閉了,并沒有啟動內(nèi)置的 web 服務(wù)器,也沒有監(jiān)聽任何端口。接下來我們看看其他兩種類型 web 的啟動日志都是怎么樣的。
啟動 Servlet web

通過啟動日志我們可以看到這里啟動了內(nèi)置的 Tomcat Servlet 服務(wù)器,監(jiān)聽了 8080 端口,應(yīng)用程序并不會像 None 類型一樣,啟動后就自動關(guān)閉。
啟動 Reactive web

通過啟動日志我們可以看到,這里啟動了內(nèi)置的 Netty 服務(wù)器,并監(jiān)聽在 8080 端口上(如果啟動失敗記得把上面 servlet web 關(guān)閉,不然端口會沖突)。
三種類型的服務(wù)我們都成功啟動了,那么接下來的問題就是 SpringBoot 是如何判斷出該使用哪種類型的呢?
這三個(gè)服務(wù)我們只有依賴不一樣,很明顯肯定和依賴有關(guān)系,接下來我們就來研究一下 SpringBoot 是如何實(shí)現(xiàn)的。
SpringBoot Web 類型推斷原理
我們在 main 方法中點(diǎn)擊 run 方法,
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}在構(gòu)造函數(shù)中我們可以看到其中有這么一行 this.webApplicationType = WebApplicationType.deduceFromClasspath();根據(jù)屬性名稱我們可以推斷,web 類型就是根據(jù) WebApplicationType.deduceFromClasspath(); 這個(gè)靜態(tài)方法來判斷的。接下來我們看下這個(gè)方法的細(xì)節(jié)。


如上圖所示,可以看到 SpringBoot 底層是通過 ClassUtils.isPresent() 方法來判斷對應(yīng)的 web 類型類是否存在來判斷 web 類型的。
在前類路徑下面如果當(dāng) org.springframework.web.reactive.DispatcherHandler 存在而且 org.springframework.web.servlet.DispatcherServlet 和 org.glassfish.jersey.servlet.ServletContainer 都不存在的時(shí)候說明當(dāng)前應(yīng)用 web 類型為 Reactive。
當(dāng) javax.servlet.Servlet 和 org.springframework.web.context.ConfigurableWebApplicationContext 任何一個(gè)不存在的時(shí)候,就說明當(dāng)前應(yīng)用是 None 類型非 web 應(yīng)用。否則當(dāng)前應(yīng)用就為 Servlet 類型。
而我們再看這個(gè) ClassUtils.isPresent() 方法,可以發(fā)現(xiàn)底層是通過 className 在類路徑上加載對應(yīng)的類,如果存在則返回 true,如果不存在則返回 false。

因此這也解釋了為什么我們在 pom 文件中只要加入對應(yīng)的依賴就可以直接得到相應(yīng)的 web 類型了,因?yàn)楫?dāng)我們在 pom 中加入相應(yīng)的依賴過后,類路徑里面就存在了前面判斷的對應(yīng)的類,再通過 ClassUtils.isPresent() 就判斷出來當(dāng)前應(yīng)用屬于那種 web 類型了。
內(nèi)置服務(wù)器是如何創(chuàng)建的
知道了 SpringBoot 是如何進(jìn)行 web 類型推斷的,那么接下來一個(gè)問題就是 SpringBoot 是如何根據(jù) web 類型進(jìn)行相應(yīng)內(nèi)置 web 服務(wù)器的啟動的呢?這里我們以 Reactive web 為例進(jìn)行調(diào)試追蹤。
首先我們在 SpringApplication 的 run 方法 createApplicationContext() 下一行打斷點(diǎn),可以發(fā)現(xiàn)創(chuàng)建成功的 context 類型為 AnnotationConfigReactiveWebServerApplicationContext 很明顯在這一步的時(shí)候就已經(jīng)根據(jù)類型推斷得到了當(dāng)前的應(yīng)用 web 類型為 Reactive,并且根據(jù) web 類型創(chuàng)建出了對應(yīng)的 ApplicationContext。

緊接著我們進(jìn)入 org.springframework.boot.SpringApplication#refreshContext 方法,最后我們可以進(jìn)入到 org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext#refresh 方法中,因?yàn)?AnnotationConfigReactiveWebServerApplicationContext 繼承了 ReactiveWebServerApplicationContext。

繼續(xù)通過引用關(guān)系,我們可以找到 org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext#onRefresh 方法,而在這個(gè)方法里面我們就會發(fā)現(xiàn)了如下代碼,此處就會創(chuàng)建一個(gè) webServer。

具體創(chuàng)建的方法在 WebServerManager 里面,跟著繼續(xù)往下找我們可以找到 createHttpServer() 方法,在 createHttpServer() 方法中就創(chuàng)建了 HttpServer 并且綁定了默認(rèn)的端口 8080。具體過程,如下幾張接入所示,感興趣的可以自行跟蹤 debug,至此一個(gè) Reactive 內(nèi)置服務(wù)器就創(chuàng)建成功了,同樣的 Servlet 服務(wù)器也是類似的。




總結(jié)
Spring 的出現(xiàn)給 Java 程序員帶來了春天,而 SpringBoot 框架的出現(xiàn)又極大的加速了程序員的開發(fā)效率,然而很多時(shí)候我們在使用她的便利的同時(shí)會缺少對于底層系統(tǒng)實(shí)現(xiàn)的把握,希望這篇文章弄幫助大家對 SpringBoot 產(chǎn)生更多的理解。
到此這篇關(guān)于SpringBoot 的 web 類型推斷詳解的文章就介紹到這了,更多相關(guān)SpringBoot 的 web 類型推斷內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot入坑筆記之spring-boot-starter-web 配置文件的使用
- 運(yùn)用springboot搭建并部署web項(xiàng)目的示例
- SpringBoot調(diào)用第三方WebService接口的操作技巧(.wsdl與.asmx類型)
- SpringBoot中配置Web靜態(tài)資源路徑的方法
- springboot整合cxf發(fā)布webservice以及調(diào)用的方法
- SpringBoot webSocket實(shí)現(xiàn)發(fā)送廣播、點(diǎn)對點(diǎn)消息和Android接收
- SpringBoot集成WebSocket實(shí)現(xiàn)前后端消息互傳的方法
- IDEA上面搭建一個(gè)SpringBoot的web-mvc項(xiàng)目遇到的問題
- SpringBoot+Websocket實(shí)現(xiàn)一個(gè)簡單的網(wǎng)頁聊天功能代碼
- springboot websocket簡單入門示例
相關(guān)文章
Springmvc應(yīng)用Mongodb分頁實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了Springmvc應(yīng)用Mongodb分頁實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11
Spring websocket并發(fā)發(fā)送消息異常的解決
本文主要介紹了 Spring websocket并發(fā)發(fā)送消息異常的解決,當(dāng)多個(gè)線程同時(shí)嘗試通過 WebSocket 會話發(fā)送消息時(shí),會拋出異常,下面就來解決一下,感興趣的可以了解一下2023-09-09
關(guān)于@ResponseBody 默認(rèn)輸出的誤區(qū)的解答
這篇文章主要介紹了關(guān)于@ResponseBody 默認(rèn)輸出的誤區(qū)的解答,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
IDEA 2020.1打開時(shí)閃退的問題及解決方法(完美解決方法)
這篇文章主要介紹了IDEA 2020.1打開時(shí)閃退問題及解決方法,本文給大家分享我的處理方案,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04
SpringBoot創(chuàng)建JSP登錄頁面功能實(shí)例代碼
這篇文章主要介紹了SpringBoot創(chuàng)建JSP登錄頁面功能實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-04-04
spring?boot之使用spring?data?jpa的自定義sql方式
這篇文章主要介紹了spring?boot之使用spring?data?jpa的自定義sql方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12

