Spring 是如何支持 Servlet 3.0 的?

寫在前面:

ServletContainerInitializer 也是 Servlet 3.0 新增的一個接口,容器在啓動時使用 JAR 服務 API(JAR Service API) 來發現 ServletContainerInitializer 的實現類,並且容器將 WEB-INF/lib 目錄下 JAR 包中的類都交給該類的 #onStartup(...) 方法處理,我們通常需要在該實現類上使用 @HandlesTypes 註解來指定希望被處理的類,過濾掉不希望給 #onStartup(...) 處理的類。

正文:

回到我們的 spring 全家桶,可能已經忘了具體是什麼時候開始不寫 web.xml 了,我只知道現在的項目已經再也看不到它了,spring 又是如何支持 servlet3.0 規範的呢?

尋找 spring 中 ServletContainerInitializer 的實現類並不困難,可以迅速定位到 org.springframework.web.SpringServletContainerInitializer 該實現類。

查看其 java doc,描述如下:

Servlet 3.0 {@link ServletContainerInitializer} designed to support code-based configuration of the servlet container using Spring’s {@link WebApplicationInitializer} SPI as opposed to (or possibly in combination with) the traditional {@code web.xml}-based approach.

注意我在源碼中標註兩個序號,這對於我們理解 spring 裝配 servlet 的流程來說非常重要。

  1. <1> 英文註釋是 spring 源碼中自帶的,它提示我們由於 servlet 廠商實現的差異,onStartup 方法會加載我們本不想處理的 class,所以進行了特判。另外,也要注意下 @HandlesTypes(WebApplicationInitializer.class) 註解,如果廠商正確的實現 @HandlesTypes 的邏輯,傳入的 Set<Class<?>> webAppInitializerClasses 都是 WebApplicationInitializer 對象。
  2. <2> spring 與我們之前的 demo 不同,並沒有在 SpringServletContainerInitializer 中直接對 servlet 和 filter 進行註冊,而是委託給了一個陌生的類 org.springframework.web.WebApplicationInitializer 。WebApplicationInitializer 類便是 spring 用來初始化 web 環境的委託者類,它通常有三個實現類:

 

你一定不會對 DispatcherServlet 感到陌生,AbstractDispatcherServletInitializer#registerDispatcherServlet 便是無 web.xml 前提下創建 DispatcherServlet 的關鍵代碼。代碼如下:

<1> 處,調用 #createServletApplicationContext() 方法,創建 WebApplicationContext 對象。代碼如下:

  1. 該方法由子類 AbstractAnnotationConfigDispatcherServletInitializer 重寫,並且創建的 WebApplicationContext 的子類 AnnotationConfigWebApplicationContext 對象。

<2> 處,調用 #createDispatcherServlet(WebApplicationContext servletAppContext) 方法,創建 FrameworkServlet 對象。代碼如下:

  1. 創建 FrameworkServlet 的子類 DispatcherServlet 對象。
  2. 另外,比較有趣的是傳入的 servletAppContext 方法參數,這就是該 DispatcherServlet 的 Servlet WebApplicationContext 容器

然後,我們可以去項目中尋找一下 org.springframework:spring-web:version 的依賴,它下面就存在一個 ServletContainerInitializer 的擴展,指向了 SpringServletContainerInitializer,這樣只要在 servlet 3.0 環境下部署,spring 便可以自動加載進行初始化:

注意,上述這一切特性從 spring 3 就已經存在了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章