亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

Tomcat源碼分析——請求原理分析(上)

2019-11-14 15:25:15
字體:
來源:轉載
供稿:網友

前言

  談起Tomcat的誕生,最早可以追溯到1995年。近20年來,Tomcat始終是使用最廣泛的Web服務器,由于其使用java語言開發,所以廣為Java程序員所熟悉。很多人早期的J2EE項目,由程序員自己實現jsp頁面或者Servlet接受請求,后來借助Struts1、Struts2、SPRing等中間件后,實際也是利用Filter或者Servlet處理請求,大家肯定要問了,這些Servlet處理的請求來自哪里?Tomcat作為Web服務器是怎樣將HTTP請求交給Servlet的呢?

  本文就Tomcat對HTTP的請求處理細節進行分析。

  提示:閱讀本文前,請確保首先理解了《Tomcat源碼分析——生命周期管理》中的內容。

Connector的初始化

  根據《Tomcat源碼分析——生命周期管理》一文的內容,我們知道Tomcat中有很多容器,包括Server、Service、Connector等。其中Connector正是與HTTP請求處理相關的容器。Service是Server的子容器,而Connector又是Service的子容器。那么這三個容器的初始化順序為:Server->Service->Connector。Connector的實現分為以下幾種:

  • Http Connector:基于HTTP協議,負責建立HTTP連接。它又分為BIO Http Connector與NIO Http Connector兩種,后者提供非阻塞IO與長連接Comet支持。
  • AJP Connector:基于AJP協議,AJP是專門設計用于Tomcat與HTTP服務器通信定制的協議,能提供較高的通信速度和效率。如與Apache服務器集成時,采用這個協議。
  • APR HTTP Connector:用C實現,通過JNI調用的。主要提升對靜態資源(如HTML、圖片、CSS、JS等)的訪問性能?,F在這個庫已獨立出來可用在任何項目中。由于APR性能較前兩類有很大提升,所以目前是Tomcat的默認Connector。

現在我們直接來看Connector的initInternal方法吧,見代碼清單1。

代碼清單1

    @Override    protected void initInternal() throws LifecycleException {        super.initInternal();                // Initialize adapter        adapter = new CoyoteAdapter(this);        protocolHandler.setAdapter(adapter);        IntrospectionUtils.setProperty(protocolHandler, "jkHome",                                       System.getProperty("catalina.base"));        onameProtocolHandler = register(protocolHandler,                createObjectNameKeyProperties("ProtocolHandler"));                mapperListener.setDomain(getDomain());        onameMapper = register(mapperListener,                createObjectNameKeyProperties("Mapper"));    }

 

代碼清單1說明了Connector的初始化步驟如下:

步驟一 構造網絡協議處理的CoyoteAdapter

  代碼清單1構造了CoyoteAdapter對象,并且將其設置為ProtocolHandler的Adapter。ProtocolHandler是做什么的呢?Tomcat處理HTTP請求,需要有一個ServerSocket監聽網絡端口來完成任務。接口ProtocolHandler被設計成控制網絡端口監聽組件運行,負責組件的生命周期控制,這個接口實際并沒有定義網絡端口監聽功能的規范,而是用于負責維護組件的生命周期。從ProtocolHandler的名字來看,它應該是網絡協議的處理者,但它實際不負責這個功能,而是將其交給org.apache.coyote.Adapter來完成,這么設計估計是為了方便維護和拓展新功能。Http11Protocol是ProtocolHandler接口的一個實現(是Connector的默認處理協議),被設計用來處理HTTP1.1網絡協議的請求,通過該類可以完成在某個網絡端口上面的監聽,同時以HTTP1.1的協議來解析請求內容,然后將請求傳遞到Connector所寄居的Container容器pipeline流水工作線上處理。此處的ProtocolHandler是何時生成的呢?還記得《TOMCAT源碼分析——SERVER.xml文件的加載與解析》一文中的Digester和Rule嗎?Digester在解析到<Connector>標簽的時候,會執行startElement方法,startElement中會調用Rule的begin(String namespace, String name, Attributes attributes)方法,Connector對應的Rule包括ConnectorCreateRule,ConnectorCreateRule的begin方法的實現見代碼清單2。

代碼清單2

 

    @Override    public void begin(String namespace, String name, Attributes attributes)            throws Exception {        Service svc = (Service)digester.peek();        Executor ex = null;        if ( attributes.getValue("executor")!=null ) {            ex = svc.getExecutor(attributes.getValue("executor"));        }        Connector con = new Connector(attributes.getValue("protocol"));        if ( ex != null )  _setExecutor(con,ex);                digester.push(con);    }

 

代碼清單2中調用了Connector的構造器,傳遞的參數為屬性protocol。我們知道server.xml中的Connector有兩個:

    <Connector port="8080" protocol="HTTP/1.1"                connectionTimeout="20000"                redirectPort="8443" />    <!-- Define an AJP 1.3 Connector on port 8009 -->    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

我們看看Connector的構造器實現,見代碼清單3。

代碼清單3

    public Connector(String protocol) {        setProtocol(protocol);        // Instantiate protocol handler        try {            Class<?> clazz = Class.forName(protocolHandlerClassName);            this.protocolHandler = (ProtocolHandler) clazz.newInstance();        } catch (Exception e) {            log.error                (sm.getString                 ("coyoteConnector.protocolHandlerInstantiationFailed", e));        }    }

setProtocol方法(見代碼清單4)根據protocol參數的不同,調用setProtocolHandlerClassName方法(見代碼清單5)設置protocolHandlerClassName屬性。以HTTP/1.1為例,由于默認情況下Apr不可用,所以protocolHandlerClassName會被設置為org.apache.coyote.http11.Http11Protocol,那么反射生成的protocolHandler就是Http11Protocol實例。Tomcat默認還會配置協議是AJP/1.3的Connector,那么此Connector的protocolHandler就是org.apache.coyote.ajp.AjpProtocol。

 代碼清單4

    /**     * Set the Coyote protocol which will be used by the connector.     *     * @param protocol The Coyote protocol name     */    public void setProtocol(String protocol) {        if (AprLifecycleListener.isAprAvailable()) {            if ("HTTP/1.1".equals(protocol)) {                setProtocolHandlerClassName                    ("org.apache.coyote.http11.Http11AprProtocol");            } else if ("AJP/1.3".equals(protocol)) {                setProtocolHandlerClassName                    ("org.apache.coyote.ajp.AjpAprProtocol");            } else if (protocol != null) {                setProtocolHandlerClassName(protocol);            } else {                setProtocolHandlerClassName                    ("org.apache.coyote.http11.Http11AprProtocol");            }        } else {            if ("HTTP/1.1".equals(protocol)) {                setProtocolHandlerClassName                    ("org.apache.coyote.http11.Http11Protocol");            } else if ("AJP/1.3".equals(protocol)) {                setProtocolHandlerClassName                    ("org.apache.coyote.ajp.AjpProtocol");            } else if (protocol != null) {                setProtocolHandlerClassName(protocol);            }        }    }

代碼清單5

    public void setProtocolHandlerClassName(String protocolHandlerClassName) {        this.protocolHandlerClassName = protocolHandlerClassName;    }

 

除此之外,ProtocolHandler還有其它實現,如圖1所示。

 

圖1  ProtocolHandler類繼承體系

圖1中有關ProtocolHandler的實現類都在org.apache.coyote包中 。前面所說的BIO Http Connector實際就是Http11Protocol,NIO Http Connector實際就是Http11NioProtocol,AJP Connector包括AjpProtocol和AjpAprProtocol,APR HTTP Connector包括AjpAprProtocol、Http11AprProtocol,此外還有一個MemoryProtocolHandler(這個是做什么的,目前沒搞清楚,有知道的同學告訴我下?。。?。

步驟二 將ProtocolHandler、MapperListener注冊到JMX

  BIO Http Connector的ProtocolHandler(即Http11Protocol)的JMX注冊名為Catalina:type=ProtocolHandler,port=8080。BIO Http Connector的MapperListener的注冊名為Catalina:type=Mapper,port=8080。AJP Connector的ProtocolHandler(即AjpProtocol)的JMX注冊名為Catalina:type=ProtocolHandler,port=8009。AJP Connector的MapperListener的注冊名為Catalina:type=Mapper,port=8009。有關Tomcat中JMX注冊的內容,請閱讀《TOMCAT源碼分析——生命周期管理》一文。

Connector的啟動

  根據《Tomcat源碼分析——生命周期管理》一文的內容,我們知道Tomcat中有很多容器。ProtocolHandler的初始化稍微有些特殊,Server、Service、Connector這三個容器的初始化順序為:Server->Service->Connector。值得注意的是,ProtocolHandler作為Connector的子容器,其初始化過程并不是由Connector的initInternal方法調用的,而是與啟動過程一道被Connector的startInternal方法所調用。由于本文的目的是分析請求,所以直接從Connector的startInternal方法(見代碼清單6)開始。

代碼清單6

    /**     * Begin processing requests via this Connector.     *     * @exception LifecycleException if a fatal startup error occurs     */    @Override    protected void startInternal() throws LifecycleException {        setState(LifecycleState.STARTING);        // Protocol handlers do not follow Lifecycle conventions.        // protocolHandler.init() needs to wait until the connector.start()        try {            protocolHandler.init();        } catch (Exception e) {            throw new LifecycleException                (sm.getString                 ("coyoteConnector.protocolHandlerInitializationFailed", e));        }        try {            protocolHandler.start();        } catch (Exception e) {            String errPrefix = "";            if(this.service != null) {                errPrefix += "service.getName(): /"" + this.service.getName() + "/"; ";            }            throw new LifecycleException                (errPrefix + " " + sm.getString                 ("coyoteConnector.protocolHandlerStartFailed", e));        }        // MapperListener doesn't follow Lifecycle conventions either gja        mapperListener.init();    }

代碼清單6說明了Connector的startInternal方法的執行順序如下:

  1. 將Connector容器的狀態更改為啟動中(LifecycleState.STARTING);
  2. 初始化ProtocolHandler;
  3. 啟動ProtocolHandler;
  4. 初始化MapperListener。

初始化ProtocolHandler

  簡單起見,我們以Http11Protocol為例剖析ProtocolHandler的init方法,其實現見代碼清單7。

代碼清單7

    @Override    public void init() throws Exception {        ((JIoEndpoint)endpoint).setName(getName());        ((JIoEndpoint)endpoint).setHandler(cHandler);        // Verify the validity of the configured socket factory        try {            if (isSSLEnabled()) {                sslImplementation =                    SSLImplementation.getInstance(sslImplementationName);                socketFactory = sslImplementation.getServerSocketFactory();                ((JIoEndpoint)endpoint).setServerSocketFactory(socketFactory);            } else if (socketFactoryName != null) {                socketFactory = (ServerSocketFactory) Class.forName(socketFactoryName).newInstance();                ((JIoEndpoint)endpoint).setServerSocketFactory(socketFactory);            }        } catch (Exception ex) {            log.error(sm.getString("http11protocol.socketfactory.initerror"),                      ex);            throw ex;        }        if (socketFactory!=null) {            Iterator<String> attE = attributes.keySet().iterator();            while( attE.hasNext() ) {                String key = attE.next();                Object v=attributes.get(key);                socketFactory.setAttribute(key, v);            }        }                try {            endpoint.init();        } catch (Exception ex) {            log.error(sm.getString("http11protocol.endpoint.initerror"), ex);            throw ex;        }        if (log.isInfoEnabled())            log.info(sm.getString("http11protocol.init", getName()));    }

從代碼清單7看到,Http11Protocol的初始化步驟如下:

步驟一 設置JIoEndpoint的名稱

  JIoEndpoint的名稱默認為http-8080,這里的JIoEndpoint是在調用Http11Protocol的構造器時創建的,Http11Protocol的構造器中還設置了socket的延遲關閉選項soLingerOn、socket的延時關閉秒數soLingerTime、socket連接超時時間soTimeout、提高socket性能的tcpNoDelay等選項,見代碼清單8。

代碼清單8

    public Http11Protocol() {        endpoint = new JIoEndpoint();        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);        //setServerSoTimeout(Constants.DEFAULT_SERVER_SOCKET_TIMEOUT);        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);            }

步驟二 設置JIoEndpoint的Handler

  JIoEndpoint的handler被設置為cHandler,此cHandler的定義如下:

    protected Http11ConnectionHandler cHandler = new Http11ConnectionHandler(this);

步驟三 配置ServerSocketFactory

從代碼清單7看到,生成ServerSocketFactory有三種方式:
  • 如果在server.xml中配置Connector時指定了SSLEnabled="true"的屬性,那么創建帶有SSL(Secure Sockets Layer 安全套接層)的ServerSocketFactory;

  • 如果Http11Protocol指定了socketFactoryName,則使用socketFactoryName反射生成ServerSocketFactory實例;
  • 如果不滿足以上2個條件,那么JIoEndpoint的init方法(見代碼清單9)將創建ServerSocketFactory。當SSLEnabled="true"時,JIoEndpoint的init方法還會給ServerSocketFactory設置一些SSL相關的屬性。最后使用此ServerSocketFactory創建serverSocket。此外,acceptorThreadCount屬性用于指定接受連接的線程數,可以通過給Connector設置acceptorThreadCount屬性進行調整,默認值為1。

代碼清單9

    @Override    public void init()        throws Exception {        if (initialized)            return;                // Initialize thread count defaults for acceptor        if (acceptorThreadCount == 0) {            acceptorThreadCount = 1;        }        if (serverSocketFactory == null) {            serverSocketFactory = ServerSocketFactory.getDefault();        }        if (isSSLEnabled()) {            serverSocketFactory.setAttribute(SSL_ATTR_ALGORITHM,                    getAlgorithm());            serverSocketFactory.setAttribute(SSL_ATTR_CLIENT_AUTH,                    getClientAuth());            serverSocketFactory.setAttribute(SSL_ATTR_KEYSTORE_FILE,                    getKeystoreFile());            serverSocketFactory.setAttribute(SSL_ATTR_KEYSTORE_PASS,                    getKeystorePass());            serverSocketFactory.setAttribute(SSL_ATTR_KEYSTORE_TYPE,                    getKeystoreType());            serverSocketFactory.setAttribute(SSL_ATTR_KEYSTORE_PROVIDER,                    getKeystoreProvider());            serverSocketFactory.setAttribute(SSL_ATTR_SSL_PROTOCOL,                    getSslProtocol());            serverSocketFactory.setAttribute(SSL_ATTR_CIPHERS,                    getCiphers());            serverSocketFactory.setAttribute(SSL_ATTR_KEY_ALIAS,                    getKeyAlias());            serverSocketFactory.setAttribute(SSL_ATTR_KEY_PASS,                    getKeyPass());            serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_FILE,                    getTruststoreFile());            serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_PASS,                    getTruststorePass());            serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_TYPE,                    getTruststoreType());            serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_PROVIDER,                    getTruststoreProvider());            serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_ALGORITHM,                    getTruststoreAlgorithm());            serverSocketFactory.setAttribute(SSL_ATTR_CRL_FILE,                    getCrlFile());            serverSocketFactory.setAttribute(SSL_ATTR_TRUST_MAX_CERT_LENGTH,                    getTrustMaxCertLength());            serverSocketFactory.setAttribute(SSL_ATTR_session_CACHE_SIZE,                    getSessionCacheSize());            serverSocketFactory.setAttribute(SSL_ATTR_SESSION_TIMEOUT,                    getSessionTimeout());            serverSocketFactory.setAttribute(SSL_ATTR_ALLOW_UNSAFE_RENEG,                    getAllowUnsafeLegacyRenegotiation());        }        if (serverSocket == null) {            try {                if (getAddress() == null) {                    serverSocket = serverSocketFactory.createSocket(getPort(), getBacklog());                } else {                    serverSocket = serverSocketFactory.createSocket(getPort(), getBacklog(), getAddress());                }            } catch (BindException orig) {                String msg;                if (getAddress() == null)                    msg = orig.getMessage() + " <null>:" + getPort();                else                    msg = orig.getMessage() + " " +                            getAddress().toString() + ":" + getPort();                BindException be = new BindException(msg);                be.initCause(orig);                throw be;            }        }        //if( serverTimeout >= 0 )        //    serverSocket.setSoTimeout( serverTimeout );                initialized = true;            }

啟動ProtocolHandler

  我們繼續以Http11Protocol為例,剖析ProtocolHandler的start方法,其實現見代碼清單10。

代碼清單10

    @Override    public void start() throws Exception {        if (this.domain != null) {            try {                tpOname = new ObjectName                    (domain + ":" + "type=ThreadPool,name=" + getName());                Registry.getRegistry(null, null)                    .registerComponent(endpoint, tpOname, null );            } catch (Exception e) {                log.error("Can't register endpoint");            }            rgOname=new ObjectName                (domain + ":type=GlobalRequestProcessor,name=" + getName());            Registry.getRegistry(null, null).registerComponent                ( cHandler.global, rgOname, null );        }        try {            endpoint.start();        } catch (Exception ex) {            log.error(sm.getString("http11protocol.endpoint.starterror"), ex);            throw ex;        }        if (log.isInfoEnabled())            log.info(sm.getString("http11protocol.start", getName()));    }

 從代碼清單10可以知道JIoEndpoint以Catalina:type=ThreadPool,name=http-8080注冊到JMX,cHandler.global(Http11ConnectionHandler的對象屬性,類型為RequestGroupInfo)以Catalina:type=GlobalRequestProcessor,name=http-8080注冊到JMX。最后調用JIoEndpoint的start方法(見代碼清單11)接受請求的創建線程池并創建一定數量的接收請求線程。

代碼清單11

    @Override    public void start() throws Exception {        // Initialize socket if not done before        if (!initialized) {            init();        }        if (!running) {            running = true;            paused = false;            // Create worker collection            if (getExecutor() == null) {                createExecutor();            }            // Start acceptor threads            for (int i = 0; i < acceptorThreadCount; i++) {                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);                acceptorThread.setPriority(threadPriority);                acceptorThread.setDaemon(getDaemon());                acceptorThread.start();            }        }    }

從代碼清單11看出JIoEndpoint的start方法的執行步驟如下:

步驟一 對JIoEndpoint做初始化檢查

  這一步實際就是判斷是否已經初始化(即initialized是否為true),如果沒有初始化則需要調用JIoEndpoint的init方法進行初始化。

步驟二 創建線程池與任務隊列

  如果JIoEndpoint尚未處于運行中(即running等于true),才會創建線程池和任務隊列。如果尚未創建線程池(即調用getExecutor方法等于null),則需要調用createExecutor方法(見代碼清單12)創建線程池和任務隊列TaskQueue。

代碼清單12

    public void createExecutor() {        internalExecutor = true;        TaskQueue taskqueue = new TaskQueue();        TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());        executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);        taskqueue.setParent( (ThreadPoolExecutor) executor);    }

  

步驟三 創建接收請線程

  如果JIoEndpoint尚未處于運行中(即running等于true),才會創建接收請求線程。從代碼清單11可以看出接收請求線程的數量主要由acceptorThreadCount控制,代碼清單9已經告訴我們acceptorThreadCount的默認值為1,但是我們可以通過給Connector增加acceptorThreadCount屬性來修改接收請求線程的數量。這些接收請求線程的主要工作由Acceptor完成,Acceptor的實質是一個Runnable,見代碼清單13。

代碼清單13

    /**     * Server socket acceptor thread.     */    protected class Acceptor implements Runnable {        /**         * The background thread that listens for incoming TCP/IP connections and         * hands them off to an appropriate processor. gja         */        public void run() {            // Loop until we receive a shutdown command            while (running) {                // Loop if endpoint is paused                while (paused) {                    try {                        Thread.sleep(1000);                    } catch (InterruptedException e) {                        // Ignore                    }                }                // Accept the next incoming connection from the server socket                try {                    Socket socket = serverSocketFactory.acceptSocket(serverSocket);                    serverSocketFactory.initSocket(socket);                    // Hand this socket off to an appropriate processor                    if (!processSocket(socket)) {                        // Close socket right away                        try {                            socket.close();                        } catch (IOException e) {                            // Ignore                        }                    }                }catch ( IOException x ) {                    if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);                } catch (Throwable t) {                    log.error(sm.getString("endpoint.accept.fail"), t);                }                // The processor will recycle itself when it finishes            }        }    }

 

初始化MapperListener

  MapperListener的init方法用于初始化,見代碼清單14。

代碼清單14

 

    /**     * Initialize associated mapper.     */    public void init() {        // Find any components that have already been initialized since the        // MBean listener won't be notified as those components will have        // already registered their MBeans jiaan        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);                // Registering the host will register the context and wrappers                registerHost(host);            }        }    }

 

從代碼清單14看到MapperListener的初始化步驟如下:

步驟一 查找默認Host

  StandardService的子容器包括:StandardEngine、Connector和Executor。MapperListener本身會持有Connector,所以可以通過各個容器的父子關系,找到Connector的同級容器StandardEngine。StandardHost是StandardEngine的子容器,Engine和Host的默認配置如下:

    <Engine name="Catalina" defaultHost="localhost">      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"             resourceName="UserDatabase"/>      <Host name="localhost"  appBase="webapps"            unpackWARs="true" autoDeploy="true">        <Valve className="org.apache.catalina.valves.accessLogValve" directory="logs"                 prefix="localhost_access_log." suffix=".txt"               pattern="%h %l %u %t &quot;%r&quot; %s %b" resolveHosts="false"/>      </Host>    </Engine>

findDefaultHost方法(見代碼清單15)可以獲取上面配置中的默認Host,Engine元素的defaultHost屬性值必須要與配置的某個Host元素的name屬性值相同。如果defaultHost的屬性值配置無誤,則會添加為MapperListener的Mapper對象屬性的defaultHostName。

代碼清單15

    private void findDefaultHost() {        Engine engine = (Engine) connector.getService().getContainer();        String defaultHost = engine.getDefaultHost();        boolean found = false;        if (defaultHost != null && defaultHost.length() >0) {            Container[] containers = engine.findChildren();                        for (Container container : containers) {                Host host = (Host) container;                if (defaultHost.equalsIgnoreCase(host.getName())) {                    found = true;                    break;                }                                String[] aliases = host.findAliases();                for (String alias : aliases) {                    if (defaultHost.equalsIgnoreCase(alias)) {                        found = true;                        break;                    }                }            }        }        if(found) {            mapper.setDefaultHostName(defaultHost);        } else {            log.warn(sm.getString("mapperListener.unknownDefaultHost",                    defaultHost));        }    }

 

 步驟二 將Host及其子容器Context,Context的子容器Wrapper注冊到MapperListener的Mapper對象

  Mapper的數據結構,見代碼清單16。

                          代碼清單16
    /**     * Array containing the virtual hosts definitions.     */    protected Host[] hosts = new Host[0];    /**     * Default host name.     */    protected String defaultHostName = null;    /**     * Context associated with this wrapper, used for wrapper mapping.     */    protected Context context = new Context();    protected static abstract class MapElement {        public String name = null;        public Object object = null;    }    protected static final class Host        extends MapElement {        public ContextList contextList = null;    }    protected static final class ContextList {        public Context[] contexts = new Context[0];        public int nesting = 0;    }    protected static final class Context        extends MapElement {        public String path = null;        public String[] welcomeResources = new String[0];        public javax.naming.Context resources = null;        public Wrapper defaultWrapper = null;        public Wrapper[] exactWrappers = new Wrapper[0];        public Wrapper[] wildcardWrappers = new Wrapper[0];        public Wrapper[] extensionWrappers = new Wrapper[0];        public int nesting = 0;    }    protected static class Wrapper        extends MapElement {        public String path = null;        public boolean jspWildCard = false;    }

根據代碼清單16,我們知道Mapper中維護著一個Host數組,每個Host中有一個ContextList,這個ContextList中維護著一個Context數組。每個Context維護著一個defaultWrapper,三個Wrapper數組(exactWrappers、wildcardWrappers、extensionWrappers)。下面對Host、Context及Wrapper進行功能上的介紹:

  • Host:代表一個虛擬主機,各Host的name不能相同,appBase代表各虛擬主機的應用發布位置;
  • Context:代表一個應用,Context可以根據應用的/WEB-INF/web.xml文件中定義的servlet來處理請求。一個Host下可以有多個Context;
  • Wrapper: 代表一個Servlet或者jsp,它負責管理一個 Servlet,包括的 Servlet 的裝載、初始化、執行以及資源回收。

 以我本地為例,注冊到Mapper中的Host及其子容器如圖2所示。

圖2  注冊到Mapper中的Host及其Context子容器

 圖2說明Host內一共5個Context,由于我的Tomcat是從svn拉下來的,所以webapps目錄下的.svn文件夾也是一個Context,除了這個天外來客,我將其它與請求有關的容器整理后用圖3來展示。

圖3  我本地的Host、Context及Wrapper

  至此,Tomcat中為請求處理的準備工作已經完成。有關請求的處理過程請繼續閱讀《Tomcat源碼分析——請求原理分析(中)》一文。


如需轉載,請標明本文作者及出處——作者:jiaan.gja,本文原創首發:博客園,原文鏈接:http://www.49028c.com/jiaan-geng/p/4875249.html

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久五月情影视| 欧美美女15p| 精品久久久久久中文字幕一区奶水| 久久精品欧美视频| 久久99国产精品久久久久久久久| 日韩综合中文字幕| 久久精品视频在线观看| 久久视频免费观看| 日韩视频第一页| 欧美在线免费视频| 色综合色综合久久综合频道88| 欧美成人免费观看| 亚洲男人第一av网站| 97视频色精品| 日韩美女在线看| 亚洲视频在线免费观看| 亚洲一区av在线播放| 欧美一二三视频| 久久天天躁狠狠躁夜夜爽蜜月| 日韩亚洲一区二区| 欧美国产日产韩国视频| 国产欧美精品xxxx另类| 精品国产91久久久| 日韩女优人人人人射在线视频| 欧美大奶子在线| 国产成人精品a视频一区www| 日本高清不卡的在线| 久久精品色欧美aⅴ一区二区| 国产精品第七影院| 亚洲综合国产精品| 日韩欧美综合在线视频| 日韩中文字幕在线视频| 国产精品一区二区久久久久| 欧美成人一区在线| 亚洲成人av资源网| 伊人久久久久久久久久久| 欧美性猛交xxxx乱大交极品| 欧美专区国产专区| 中文字幕一区电影| 国产主播欧美精品| 国产亚洲美女精品久久久| 中日韩午夜理伦电影免费| 久久人人爽人人爽人人片亚洲| 亚洲男人av电影| 日韩一级裸体免费视频| 久久久爽爽爽美女图片| 亚洲人高潮女人毛茸茸| 日韩av大片在线| 欧美高清视频在线| 亚洲影影院av| 日韩激情av在线免费观看| 亚洲成人a级网| 国产精品久久久久秋霞鲁丝| 性欧美暴力猛交69hd| 久久久久久网站| 久久精品色欧美aⅴ一区二区| 亚洲毛茸茸少妇高潮呻吟| 精品久久久久久久久久久久久| 欧美成年人视频网站欧美| 亚洲影院色在线观看免费| 中文字幕一精品亚洲无线一区| 在线免费观看羞羞视频一区二区| 亚洲午夜国产成人av电影男同| 欧美性猛交xxxx富婆弯腰| 热99久久精品| 亚洲va国产va天堂va久久| 久久精品国产久精国产一老狼| 成人免费观看网址| 国产一区视频在线播放| 国产精品综合久久久| 欧美精品999| 色偷偷噜噜噜亚洲男人的天堂| 欧美性xxxxxx| 亚洲欧洲黄色网| 亚洲精品免费av| 亚洲欧美国产日韩中文字幕| 久久精品视频亚洲| 亚洲欧洲激情在线| 亚洲欧美精品在线| 不卡av电影在线观看| 日韩av在线影院| 国产精品一区二区久久久| 欧美激情免费看| 日韩精品久久久久久福利| 精品无人国产偷自产在线| 欧美成人精品三级在线观看| 欧美一级淫片aaaaaaa视频| 日韩av在线最新| 欧美日韩人人澡狠狠躁视频| 尤物yw午夜国产精品视频明星| 亚洲欧美日本精品| 亚洲美女又黄又爽在线观看| 亚洲第五色综合网| 亚洲精美色品网站| 草民午夜欧美限制a级福利片| 伊人久久男人天堂| 欧美性极品xxxx娇小| 日韩美女视频在线观看| 欧美激情va永久在线播放| 少妇久久久久久| 日韩一区二区欧美| 成人在线小视频| 国产精品视频99| 精品久久久久久电影| 亚洲一区二区三区成人在线视频精品| 亚洲电影免费观看高清完整版在线| 日韩av网站导航| 91在线观看免费高清| 亚洲精品国产精品国自产在线| 日韩在线观看电影| 黑人极品videos精品欧美裸| 久久久久国产一区二区三区| 国产日韩欧美日韩| 中文字幕欧美在线| 岛国视频午夜一区免费在线观看| 成人黄色大片在线免费观看| 中文字幕欧美在线| 亚洲美女视频网站| 日本免费一区二区三区视频观看| 亚洲区中文字幕| 亚洲精品国产精品国产自| 亚洲a在线观看| 欧美福利视频在线| 国内精品模特av私拍在线观看| www.久久色.com| 在线观看不卡av| 欧美裸体xxxx极品少妇软件| 亚洲另类欧美自拍| 午夜精品久久久久久99热| 国产成人激情视频| 欧美在线免费视频| 欧美亚洲激情视频| 日韩禁在线播放| 亚洲成人a**站| 亚洲精品免费在线视频| 欧美猛男性生活免费| 日本在线观看天堂男亚洲| 日韩av电影手机在线观看| 国产综合久久久久久| 中文字幕成人在线| 91国产高清在线| 91影院在线免费观看视频| 国产精品成人一区二区三区吃奶| 欧美成人在线网站| 国产精品视频yy9099| 国产色综合天天综合网| 亚洲成人动漫在线播放| 伊人亚洲福利一区二区三区| 亚洲精品一区av在线播放| 亚洲一区av在线播放| 中文字幕亚洲情99在线| 亚洲尤物视频网| 国产午夜精品免费一区二区三区| 欧美性猛交xxxx富婆弯腰| www.日本久久久久com.| 奇米影视亚洲狠狠色| 91亚洲国产成人精品性色| 欧美高清理论片| 九九热精品视频在线播放| 亚洲最大成人在线| 91精品国产综合久久久久久久久| 欧美性少妇18aaaa视频| 国产一区二区丝袜高跟鞋图片| 美女啪啪无遮挡免费久久网站|