用了這麼多年的 SpringBoot
那麼你知道什麼是 SpringBoot
的 web
型別推斷嗎?
估計很多小夥伴都不知道,畢竟平時開發做專案的時候做的都是普通的 web
專案並不需要什麼特別的瞭解,不過抱著學習的心態,阿粉今天帶大家看一下什麼是 SpringBoot
的 web
型別推斷。
既然是web
型別推斷,那我們肯定要知道 SpringBoot
支援哪些型別,然後才能分析是怎樣進行型別推斷的。
根據官方的介紹 SpringBoot
的 web
型別有三種,分別是,NONE
、SERVLET
和 REACTIVE
,定義在列舉 WebApplicationType
中,這三種型別分別代表了三種含義:
NONE
:不是一個 web
應用,不需要啟動內建的 web
伺服器;SERVLET
:基於 servlet
的 web
應用,需要啟動一個內建的 servlet
伺服器;REACTIVE
:一個 reactive
的 web
應用,需要啟動一個內建的 reactive
伺服器;public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
}
上面提到了 SpringBoot
的三種 web
型別,接下來我們先通過程式碼驗證一下,然後再分析一下 SpringBoot
是如何進行型別推斷的。
首先我們通過在 https://start.spring.io/ 快速的構建三種型別的專案,三種型別的專案設定除了依賴不一樣之外,其他都一樣,如下所示
下載後的專案檔案 pom
中對應的依賴為
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
下載後的專案檔案 pom
中對應的依賴為
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
下載後的專案檔案 pom
中對應的依賴為
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
接下來我們依次啟動三個專案看看有什麼區別,
通過啟動紀錄檔我們可以看到,在 None web
型別下,應用啟動執行後就自動關閉了,並沒有啟動內建的 web
伺服器,也沒有監聽任何埠。接下來我們看看其他兩種型別 web
的啟動紀錄檔都是怎麼樣的。
通過啟動紀錄檔我們可以看到這裡啟動了內建的 Tomcat Servlet
伺服器,監聽了 8080
埠,應用程式並不會像 None
型別一樣,啟動後就自動關閉。
通過啟動紀錄檔我們可以看到,這裡啟動了內建的 Netty
伺服器,並監聽在 8080
埠上(如果啟動失敗記得把上面 servlet web
關閉,不然埠會衝突)。
三種型別的服務我們都成功啟動了,那麼接下來的問題就是 SpringBoot
是如何判斷出該使用哪種型別的呢?
這三個服務我們只有依賴不一樣,很明顯肯定和依賴有關係,接下來我們就來研究一下 SpringBoot
是如何實現的。
我們在 main
方法中點選 run
方法,
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
在建構函式中我們可以看到其中有這麼一行 this.webApplicationType = WebApplicationType.deduceFromClasspath();
根據屬性名稱我們可以推斷,web
型別就是根據 WebApplicationType.deduceFromClasspath();
這個靜態方法來判斷的。接下來我們看下這個方法的細節。
如上圖所示,可以看到 SpringBoot
底層是通過 ClassUtils.isPresent()
方法來判斷對應的 web
型別類是否存在來判斷 web
型別的。
在前類路徑下面如果當 org.springframework.web.reactive.DispatcherHandler
存在而且 org.springframework.web.servlet.DispatcherServlet
和 org.glassfish.jersey.servlet.ServletContainer
都不存在的時候說明當前應用 web
型別為 Reactive
。
當 javax.servlet.Servlet
和 org.springframework.web.context.ConfigurableWebApplicationContext
任何一個不存在的時候,就說明當前應用是 None
型別非 web
應用。否則當前應用就為 Servlet
型別。
而我們再看這個 ClassUtils.isPresent()
方法,可以發現底層是通過 className
在類路徑上載入對應的類,如果存在則返回 true
,如果不存在則返回 false
。
因此這也解釋了為什麼我們在 pom
檔案中只要加入對應的依賴就可以直接得到相應的 web
型別了,因為當我們在 pom
中加入相應的依賴過後,類路徑裡面就存在了前面判斷的對應的類,再通過 ClassUtils.isPresent()
就判斷出來當前應用屬於那種 web
型別了。
知道了 SpringBoot
是如何進行 web
型別推斷的,那麼接下來一個問題就是 SpringBoot
是如何根據 web
型別進行相應內建 web
伺服器的啟動的呢?這裡我們以 Reactive web
為例進行偵錯追蹤。
首先我們在 SpringApplication
的 run
方法 createApplicationContext()
下一行打斷點,可以發現建立成功的 context
型別為 AnnotationConfigReactiveWebServerApplicationContext
很明顯在這一步的時候就已經根據型別推斷得到了當前的應用 web
型別為 Reactive
,並且根據 web
型別建立出了對應的 ApplicationContext
。
緊接著我們進入 org.springframework.boot.SpringApplication#refreshContext
方法,最後我們可以進入到 org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext#refresh
方法中,因為 AnnotationConfigReactiveWebServerApplicationContext
繼承了 ReactiveWebServerApplicationContext
。
繼續通過參照關係,我們可以找到 org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext#onRefresh
方法,而在這個方法裡面我們就會發現瞭如下程式碼,此處就會建立一個 webServer
。
具體建立的方法在 WebServerManager
裡面,跟著繼續往下找我們可以找到 createHttpServer()
方法,在 createHttpServer()
方法中就建立了 HttpServer
並且繫結了預設的埠 8080
。具體過程,如下幾張接入所示,感興趣的可以自行跟蹤 debug
,至此一個 Reactive
內建伺服器就建立成功了,同樣的 Servlet
伺服器也是類似的。
Spring
的出現給 Java
程式設計師帶來了春天,而 SpringBoot
框架的出現又極大的加速了程式設計師的開發效率,然而很多時候我們在使用她的便利的同時會缺少對於底層系統實現的把握,希望這篇文章弄幫助大家對 SpringBoot
產生更多的理解。
本文來自部落格園,作者:zi-you,轉載請註明原文連結:https://www.cnblogs.com/zi-you/p/17006734.html