servlet工作原理
首先提出幾個問題:
1.servlet容器是如何工作的;
2.一個Web工程在servlet容器中是如何啟動的;
3.servlet容器如何解析你在web.xml中定義的servlet;
4.用戶的請求是如何被分配給指定的servlet的;
5.servlet容器如何管理servlet生命周期;
servlet容器
servlet與servlet容器彼此依存,相互獨立發展。這是為了適應工業化生產而解耦,通過標準化接口來相互協作。所以接口是連接servlet與servlet容器的關鍵。
servlet容器作為一個獨立發展的標準化產品,種類很多,以Tomcat為例:
在Tomcat 的容器等級中,Context 容器直接管理Servlet在容器中的包裝類 Wrapper,所以Context容器如何運行將直接影響Servlet 的工作方式。
Tomcat 的容器分為4個等級:
【Container【Engine【Host【Context【Wrapper1,Wrapper2...】,Context【Wrapper1,Wrapper2...】...】】】
真正管理Servlet的容器是Context容器,一個Context對應一個web工程,在Tomcat的配置文件中可以很容易地發現這一點
如下:<Context path="/projectOne" docBase="D:\projects\projectOne" reloadable="true" />
Servlet容器的啟動過程
Tomcat7開始支持嵌入式功能,增加了一個啟動類org.apache.catalina.startup.Tomcat。創建一個實例對象並調用start方法就可以很容易地啟動Tomcat。還可以通過這個對象來增加和修改Tomcat的配置參數,如動態增加Context,Servlet等。
利用Tomcat類來管理一個新增的Context容器,選擇example中的一個工程,看看它是如何加到這個Context中的
package org.apache.catalina.webapp; import static org.junit.Assert.*; import java.io.File; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; import org.apache.tomcat.util.buf.ByteChunk; importorg.junit.Test; public class LoadApp extends TomcatBaseTest { @Test public void loadAppTest() throws Exception{ Tomcat tomcat = getTomcatInstance(); File appDir = new File(System.getProperty("tomcat.test.tomcatbuild"), "webapps/examples"); tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); tomcat.start(); ByteChunk res = getUrl("http://localhost:" + getPort()+ "/examples/servlets/servlet/HelloWorldExample"); assertTrue(res.toString().indexOf("<h1>Hello World!</h1>") > 0); } }
一個Web應用對應一個Context容器,也就是Servlet運行時的Servlet容器。添加一個Web應用時將會創建一個StandardContext容器,並且給這個容器設置必要的參數,url和path分別代表這個應用在Tomcat中的訪問路徑和這個應用實際的物理路徑,這兩個參數與Tomcat配置中的兩個參數是一致的。其中最重要的一個配置是ContextConfiig,這個類將會負責整個Web應用配置的解析工作,最後將這個Context容器加到父容器Host中。
Tomcat的addWebapp方法如下:
/** * @see #addWebapp(String, String) */ public Context addWebapp(Host host, String url, String name, String path) { silence(host, url); Context ctx = new StandardContext(); ctx.setName(name); ctx.setPath(url); ctx.setDocBase(path); ctx.addLifecycleListener(new DefaultWebXmlListener()); ctx.setConfigFile(getWebappConfigFile(path, url)); ContextConfig ctxCfg = new ContextConfig(); ctx.addLifecycleListener(ctxCfg); // prevent it from looking ( if it finds one - it‘ll have dup error ) ctxCfg.setDefaultWebXml(noDefaultWebXmlPath()); if (host == null) { getHost().addChild(ctx); } else { host.addChild(ctx); } return ctx; }
最後調用Tomcat的start方法啟動。Tomcat的啟動邏輯是基於觀察者模式設計的,所有的容器都會繼承Lifecycle接口,它管理著容器的整個生命周期,所有容器的修改和狀態的改變都會由它去通知已經註冊的Listener。Tomcat啟動的時序圖如下:
12.當Context的state改為init時,ContextConfig作為觀察者將會被通知,ContextConfig.lifecycleEvent方法將會被觸發。
13.完成對應的每個web應用的配置解析。
14.完成web應用的初始化工作
15.後臺線程,用於處理一些定時操作或者監控配置的修改。
18.啟動HTTP服務。
上圖描述了Tomcat的啟動過程中主要類之間的時序關系,下面重點關註下StandardContext容器的啟動過程:
當Context容器初始化狀態設為init時,添加到Context容器的Listener將會被調用。ContextConfig繼承了LifecycleListener接口,它在調用Tomcat.addWebapp時被加入到StandardContext容器中。ContextConfig類會負責整個web應用的配置文件的解析工作。ContextConfig的init方法主要會完成以下工作:
1.創建用於解析xml配置文件的contextDigester對象
2.讀取默認的context.xml配置文件,如果存在則解析它
3.讀取默認的Host配置文件,如果存在則解析它
4.讀取默認的Context自身的配置文件,如果存在則解析它
5.設置Context的DocBaseContextConfig的init方法完成後,Context容器就會執行startInternal方法,主要包括以下部分:
1.創建讀取資源文件的對象
2.創建ClassLoader對象
3.設置應用的工作目錄
4.啟動相關的輔助類,如logger、realm、resources等
5.修改啟動狀態,通知感興趣的觀察者(web應用的配置)
6.子容器的初始化
7.獲取ServletContext並設置必要的參數
8.初始化“load on startup”的servlet
#筆記內容來自 《深入分析 java web》
servlet工作原理