Servlet原理 三:Servlet體系結構以及工作原理

注:在網上看到一篇文章--《Servlet工作原理》,整理并做了一些筆記。

1. Servlet 體系結構

Servlet體系結構.png

我們知道 Java Web 應用是基于 Servlet 規范運轉的,那么 Servlet 本身又是如何運轉的呢?為何要設計這樣的體系結構。


Servlet頂層類.jpg

從上圖可以看出 Servlet 規范就是基于這幾個類運轉的,與 Servlet 主動關聯的是三個類,分別是 ServletConfig、ServletRequest 和 ServletResponse。這三個類都是通過容器傳遞給 Servlet 的,其中 ServletConfig 是在 Servlet 初始化時就傳給 Servlet 了,而后兩個是在請求達到時調用 Servlet 時傳遞過來的。

我們應該很清楚 ServletRequest 和 ServletResponse 在 Servlet 運行的意義,但是 ServletConfig 和 ServletContext 對 Servlet 有何價值?

仔細查看 ServletConfig 接口中聲明的方法就會發現,這些方法都是為了獲取這個 Servlet 的一些配置屬性,而這些配置屬性可能會在 Servlet 運行時被用到。

那么 ServletContext 又是干什么的呢? Servlet 的運行模式是一個典型的“握手型的交互式”運行模式。所謂“握手型的交互式”就是兩個模塊為了交換數據通常都會準備一個交易場景,這個場景一直跟隨個這個交易過程直到這個交易完成為止。這個交易場景的初始化是根據這次交易對象指定的參數來定制的,這些指定參數通常就會是一個配置類。所以對號入座,交易場景就由 ServletContext 來描述,而定制的參數集合就由 ServletConfig 來描述。而 ServletRequest 和 ServletResponse 就是要交互的具體對象了,它們通常都是作為運輸工具來傳遞交互結果。

ServletConfig 是在 Servlet init 時由容器傳過來的,那么 ServletConfig 到底是個什么對象呢?


ServletConfig.jpg

上圖可以看出 StandardWrapper 和 StandardWrapperFacade 都實現了 ServletConfig 接口,而 StandardWrapperFacade 是 StandardWrapper 門面類,所以傳給 Servlet 的是 StandardWrapperFacade 對象,這個類能夠保證從 StandardWrapper 中拿到 ServletConfig 所規定的數據,而又不把 ServletConfig 不關心的數據暴露給 Servlet。

同樣 ServletContext 也與 ServletConfig 有類似的結構,Servlet 中能拿到的 ServletContext 的實際對象也是 ApplicationContextFacade 對象。ApplicationContextFacade 同樣保證 ServletContex 只能從容器中拿到它該拿的數據,它們都起到對數據的封裝作用,它們使用的都是門面設計模式。

通過 ServletContext 可以拿到 Context 容器中一些必要信息,比如應用的工作路徑,容器支持的 Servlet 最小版本等。

Servlet 中定義的兩個 ServletRequest 和 ServletResponse 它們實際的對象又是什么呢?,我們在創建自己的 Servlet 類時通常使用的都是 HttpServletRequest 和 HttpServletResponse,它們繼承了 ServletRequest 和 ServletResponse。為何 Context 容器傳過來的 ServletRequest、ServletResponse 可以被轉化為 HttpServletRequest 和 HttpServletResponse 呢?


Request.jpg

上圖是 Tomcat 創建的 Request 和 Response 的類結構圖。Tomcat 一接受到請求首先將會創建 org.apache.coyote.Request 和 org.apache.coyote.Response,這兩個類是 Tomcat 內部使用的描述一次請求和相應的信息類它們是一個輕量級的類,它們作用就是在服務器接收到請求后,經過簡單解析將這個請求快速的分配給后續線程去處理,所以它們的對象很小,很容易被 JVM 回收。接下去當交給一個用戶線程去處理這個請求時又創建 org.apache.catalina.connector. Request 和 org.apache.catalina.connector. Response 對象。這兩個對象一直穿越整個 Servlet 容器直到要傳給 Servlet,傳給 Servlet 的是 Request 和 Response 的門面類 RequestFacade 和 RequestFacade,這里使用門面模式與前面一樣都是基于同樣的目的——封裝容器中的數據。一次請求對應的 Request 和 Response 的類轉化如下圖所示:


Request和Response的轉變.jpg

2. Servlet 如何工作

Servlet如何讓工作.png

我們已經清楚了 Servlet 是如何被加載的、Servlet 是如何被初始化的,以及 Servlet 的體系結構,現在的問題就是它是如何被調用的。

當用戶從瀏覽器向服務器發起一個請求,通常會包含如下信息:http://hostname: port /contextpath/servletpath,hostname 和 port 是用來與服務器建立 TCP 連接,而后面的 URL 才是用來選擇服務器中那個子容器服務用戶的請求。那服務器是如何根據這個 URL 來達到正確的 Servlet 容器中的呢?

Tomcat7.0 中這件事很容易解決,因為這種映射工作有專門一個類來完成的,這個就是 org.apache.tomcat.util.http.mapper,這個類保存了 Tomcat 的 Container 容器中的所有子容器的信息,當 org.apache.catalina.connector. Request 類在進入 Container 容器之前,mapper 將會根據這次請求的 hostnane 和 contextpath 將 host 和 context 容器設置到 Request 的 mappingData 屬性中。所以當 Request 進入 Container 容器之前,它要訪問那個子容器這時就已經確定了。


Request的Mapper.jpg

可能你有疑問,mapper 中怎么會有容器的完整關系,這要回到圖 2 中 19 步 MapperListener 類的初始化過程,下面是 MapperListener 的 init 方法代碼 :

public void init() { 
        findDefaultHost(); 
        Engine engine = (Engine) connector.getService().getContainer(); 
        engine.addContainerListener(this); 
        Container[] conHosts = engine.findChildren(); 
        for (Container conHost : conHosts) { 
            Host host = (Host) conHost; 
            if (!LifecycleState.NEW.equals(host.getState())) { 
                host.addLifecycleListener(this); 
                registerHost(host); 
            } 
        } 
 }

這段代碼的作用就是將 MapperListener 類作為一個監聽者加到整個 Container 容器中的每個子容器中,這樣只要任何一個容器發生變化,MapperListener 都將會被通知,相應的保存容器關系的 MapperListener 的 mapper 屬性也會修改。for 循環中就是將 host 及下面的子容器注冊到 mapper 中。


Request在容器中得路由圖.jpg

上圖描述了一次 Request 請求是如何達到最終的 Wrapper 容器的,我們現正知道了請求是如何達到正確的 Wrapper 容器,但是請求到達最終的 Servlet 還要完成一些步驟,必須要執行 Filter 鏈,以及要通知你在 web.xml 中定義的 listener。

接下去就要執行 Servlet 的 service 方法了,通常情況下,我們自己定義的 servlet 并不是直接去實現 javax.servlet.servlet 接口,而是去繼承更簡單的 HttpServlet 類或者 GenericServlet 類,我們可以有選擇的覆蓋相應方法去實現我們要完成的工作。

Servlet 的確已經能夠幫我們完成所有的工作了,但是現在的 web 應用很少有直接將交互全部頁面都用 servlet 來實現,而是采用更加高效的 MVC 框架來實現。這些 MVC 框架基本的原理都是將所有的請求都映射到一個 Servlet,然后去實現 service 方法,這個方法也就是 MVC 框架的入口。

當 Servlet 從 Servlet 容器中移除時,也就表明該 Servlet 的生命周期結束了,這時 Servlet 的 destroy 方法將被調用,做一些掃尾工作。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容