我們已經清楚了 Servlet 是如何被加載的、初始化及其體系結構,現在的問題就是它是如何被調用的.
使用者從浏覽器向伺服器發起的一個請求通常會包含如下資訊
http://hostname: port /contextpath/servletpath
- hostname 和 port:與伺服器建立 TCP 連接配接
- URL:選擇在伺服器中哪個子容器服務使用者的請求
伺服器是如何根據這個 URL 到達正确的 Servlet 容器中的呢?
-
在 Tomcat7 中這件事很容易解決,因為這種映射工作有專門的一個類來完成 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 類關系圖Servlet 如何工作
可能你有疑問,mapper 中怎麼會有容器的完整關系?
這要回到
http://upload-images.jianshu.io/upload_images/4685968-f4c4cc6126fe8e14.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700中 第 19 步 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 在容器中的路由圖
上圖描述了一次 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 方法将被調用,善後.