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

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

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

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

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

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

通過(guò)啟動(dòng)日志我們可以看到,這里啟動(dòng)了內(nèi)置的 Netty 服務(wù)器,并監(jiān)聽在 8080 端口上(如果啟動(dòng)失敗記得把上面 servlet web 關(guān)閉,不然端口會(huì)沖突)。
三種類型的服務(wù)我們都成功啟動(dòng)了,那么接下來(lái)的問題就是 SpringBoot 是如何判斷出該使用哪種類型的呢?
這三個(gè)服務(wù)我們只有依賴不一樣,很明顯肯定和依賴有關(guān)系,接下來(lái)我們就來(lái)研究一下 SpringBoot 是如何實(shí)現(xiàn)的。
SpringBoot Web 類型推斷原理
我們?cè)?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)方法來(lái)判斷的。接下來(lái)我們看下這個(gè)方法的細(xì)節(jié)。


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

因此這也解釋了為什么我們?cè)?pom 文件中只要加入對(duì)應(yīng)的依賴就可以直接得到相應(yīng)的 web 類型了,因?yàn)楫?dāng)我們?cè)?pom 中加入相應(yīng)的依賴過(guò)后,類路徑里面就存在了前面判斷的對(duì)應(yīng)的類,再通過(guò) ClassUtils.isPresent() 就判斷出來(lái)當(dāng)前應(yīng)用屬于那種 web 類型了。
內(nèi)置服務(wù)器是如何創(chuàng)建的
知道了 SpringBoot 是如何進(jìn)行 web 類型推斷的,那么接下來(lái)一個(gè)問題就是 SpringBoot 是如何根據(jù) web 類型進(jìn)行相應(yīng)內(nèi)置 web 服務(wù)器的啟動(dòng)的呢?這里我們以 Reactive web 為例進(jìn)行調(diào)試追蹤。
首先我們?cè)?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)建出了對(duì)應(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ò)引用關(guān)系,我們可以找到 org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext#onRefresh 方法,而在這個(gè)方法里面我們就會(huì)發(fā)現(xiàn)了如下代碼,此處就會(huì)創(chuàng)建一個(gè) webServer。

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




總結(jié)
Spring 的出現(xiàn)給 Java 程序員帶來(lái)了春天,而 SpringBoot 框架的出現(xiàn)又極大的加速了程序員的開發(fā)效率,然而很多時(shí)候我們?cè)谑褂盟谋憷耐瑫r(shí)會(huì)缺少對(duì)于底層系統(tǒng)實(shí)現(xiàn)的把握,希望這篇文章弄幫助大家對(duì) SpringBoot 產(chǎn)生更多的理解。
到此這篇關(guān)于SpringBoot 的 web 類型推斷詳解的文章就介紹到這了,更多相關(guān)SpringBoot 的 web 類型推斷內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(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)對(duì)點(diǎn)消息和Android接收
- SpringBoot集成WebSocket實(shí)現(xiàn)前后端消息互傳的方法
- IDEA上面搭建一個(gè)SpringBoot的web-mvc項(xiàng)目遇到的問題
- SpringBoot+Websocket實(shí)現(xiàn)一個(gè)簡(jiǎn)單的網(wǎng)頁(yè)聊天功能代碼
- springboot websocket簡(jiǎn)單入門示例
相關(guān)文章
SpringBoot中REST API 接口傳參的實(shí)現(xiàn)
我們?cè)陂_發(fā)?REST API?的過(guò)程中,經(jīng)常需要傳遞參數(shù),本文主要介紹了SpringBoot中REST API 接口傳參的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
SpringBoot2.0集成Swagger2訪問404的解決操作
這篇文章主要介紹了SpringBoot2.0集成Swagger2訪問404的解決操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09
在Java中使用redisTemplate操作緩存的方法示例
這篇文章主要介紹了在Java中使用redisTemplate操作緩存的方法示例,在Redis中可以存儲(chǔ)String、List、Set、Hash、Zset。感興趣的可以了解一下2019-01-01

