1. 程式人生 > >servlet工作原理

servlet工作原理

exce 分享 file standard 負責 fin 開始 .cn realm

首先提出幾個問題:

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;
import
org.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工作原理