Tomcat源码11

对前面的知识稍微总结以下:
Connector 组件
Connector 组件是 Tomcat 中两个核心组件之一,它的主要任务是负责接收浏览器的发过来的 tcp 连接请求,创建一个 Request 和 Response 对象分别用于和请求端交换数据,然后会产生一个线程来处理这个请求并把产生的 Request 和 Response 对象传给处理这个请求的线程,处理这个请求的线程就是 Container 组件要做的事了。

请看下面类图:




HTTP的解析就暂时先告一段落了,后面如果有再遇到新的东西,再拿出来单独详细的讲解。
今天来讲解一下Servlet容器:
Servlet容器是用来处理请求servlet资源,并为Web客户端填充response对象的模块。Servlet容器实org.apache.catalina.Container接口的实例。在Tomcat中,共有四种类型的容器,分别实:Engine,Host,Context和Wrapper。接下来重点先放在Context和Wrapper。
在分析这两个的源码前,先要做一些简单的分析:
1,首先是Container和Connector,我们知道,在连接器Connector处理完请求后,就要调用Servlet容器的invoke()方法。那么很容易想到,这两个类之间存在着关联的关系。也就是说需要将servlet容器的实例作为参数传入到连接器的setContainer()方法中,这样连接器才能调用servlet容器的invoke()方法。例如下面的代码:
HttpConnector connetcor = new HttpConnector();//生成一个连接器实例
SimpleContainer container = new SimpleContainer();//生成一个Servlet容器对象
connector.setContainer(container);//将Servlet容器对象设置到连接器中
2,对于Catalina中的servlet容器,首先需要注意的是。共有4种类型的容器,分别对应不同的概念层次。
*Engine:表示整个Catalina servlet引擎
*Host:表示包含有一个或多个Context容器的虚拟主机
*Context:表示一个Web应用程序。一个Context可以有多个Wrapper;
*Wrapper:表示一个独立的servlet

上述的每个概念层级都由org.apache.catalina包内的一个接口表示,这些接口分别是Engine,Host,Context,Wrapper,他们都继承自Container接口。这4个接口的标准实现分别是StandardEngine类,StandardHost类,StandardContext类和StandardWrapper类,他们都在org.apache.catalina.core包内。见以下类图:




部署功能性的Catalina并不是必须讲所有4种容器都包括在内。
一个人容器可以有0个或多个低层级的子容器,例如,一般情况下,一个Context实例会有一个或多个Wrapper实例,一个Host实例中会有0个或多个Context实例。但是,Wrapper类型处于层级结构的最底层,因此,它无法再包含子容器了。可以通过调用Container接口的addChild()方法向某容器添加子容器,该方法的如下:
public void addChild(Container child);
调用Container接口的removeChild()方法删除某容器中的一个子容器,remove()方法如下:
public void removeChile(Container child)
此外,Container接口也提供了findChild()方法和findChildren()方法来查找某个子容器和所有子容器的某个集合。这两个方法如下:
public Container findChild(String name);
public Container[] findChildren();
容器可以包含一些支持的组件,如载入器,记录器,管理器,领域和资源。值得注意的是,Container接口提供了getter和setter方法将这些组件与容器相关联。这些方法包括getLoader()和setLoader(),getLogger()和setLogger(),getManager()和setManager(),getRealm()和setRealm()以及getResources()和setResources()。
更有趣的是,Container接口的设计满足以下条件:在部署应用时,Tomcat管理员可以通过编辑配置文件(server.xml)来决定使用哪种容器。这是通过引用容器中的管道(pipeline)和(value)的集合实现的。这些容器里面的方法N多。但是我们现在主要先把精力放在servlet容器的invoke()方法后会发生什么事。然后,在对应小节中讨论org.apache.catalina包中的4个相关接口,Pipeline,Value,ValueContext和Contained。
管道包含该servlet容器将要调用的任务。一个阀表示一个具体的执行任务,在servlet容器的管道中,有一个基础阀,但是,可以添加任意数量的阀。阀的数量指的是额外添加的阀数量,即,不包括基础阀。有意思的是,可以通过编辑Tomcat的配置文件server.xml来动态添加阀。
管道就像过滤链一样,而阀则好似是过滤器。阀与过滤器类似,可以处理传递给它的request对象和response对象。当一个阀执行完成后,会调用下一个阀继续执行。基础阀总是最后一个执行的。
一个servlet容器可以有一个管道。当调用了容器的invoke()方法后,容器会将处理工作交由管道完成,而管道会调用其中的第一个阀开始处理。当第1个阀处理完后,它会调用后续的阀继续执行任务,直到管道中所有的阀都处理完成。下面是在管理的invoke()方法中执行的伪代码:
//invoke each value added to this pipeline
for(int i = 0;n < valves.length;n++){
    valve[i].invoke(...);
}
//then,invoke the basic valve
basicValue.invoke(...);
但是,Tomcat的设计者选择了另一个实现方法,通过引用接口org.apache.catalina.ValueContext来实现阀的遍历执行。下面是它的工作原理。
当连接器调用容器的invoke()方法后,容器中要执行的任务并没有硬编码在invoke()方法中。相反,容器会调用其管道的invoke()方法。Pipeline接口的invoke()方法的签名与Container接口的invoke()方法完全相同。如下所示:
public void invoke(Request request,Response response)throws IOException,ServletException;
下面是Container接口在org.apache.catalina.core.ContainerBase类中的invoke()方法的实现:
public void invoke(Request request,Response response)throws IOException,ServletException{
    pipeline.invoke(request,response);
}
其中pipeline是该容器中的Pipeline接口的实例
现在,管道必须保证添加到其中的所有阀及其基础阀都被调用一次,这是通过创建一个ValveContext接口实例来实现的。ValveContext是作为管道的一个内部类实现的,因此,ValveContext接口就可以访问管理的所有成员。ValveContext接口最重要的方法是invokeNext():
public void invokeNext(Request request,Response response)throws IOException,servletException;
在创建了ValueContext实例后,管道会调用ValueContext实例的invokeNext()方法,ValueContext实例会首先调用管道中的第1个阀,第1个阀执行完后,会调用后面的阀继续执行。ValveContext实例会将自身传给每个阀,因此,每个阀都可以调用ValueContext实例的invokeNext()方法。下面是Value接口的invoke()方法:
public void invokeNext(Request request,Response response)throws IOException,servletException;
Value接口的invoke()方法的实现类似如下代码:
public void invoke(Request request,Response response),ValveContext valveContext)throws IOException,ServletException{
    //pass the request and response on to the next value in our pipeline
    valueContext.invokeNext(request,response);
    //now perform what this value is supposed to do
    ..........
}
大概理论性的东西看完,下面直接进入源码吧!终于可以来看看源码的,受不了了,现在先来看看管道类
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.apache.catalina;


import java.io.IOException;
import javax.servlet.ServletException;


/**
 * <p>Interface describing a collection of Valves that should be executed
 * in sequence when the <code>invoke()</code> method is invoked.  It is
 * required that a Valve somewhere in the pipeline (usually the last one)
 * must process the request and create the corresponding response, rather
 * than trying to pass the request on.</p>
 *
 * <p>There is generally a single Pipeline instance associated with each
 * Container.  The container's normal request processing functionality is
 * generally encapsulated in a container-specific Valve, which should always
 * be executed at the end of a pipeline.  To facilitate this, the
 * <code>setBasic()</code> method is provided to set the Valve instance that
 * will always be executed last.  Other Valves will be executed in the order
 * that they were added, before the basic Valve is executed.</p>
 *
 * @author Craig R. McClanahan
 * @author Peter Donald
 * @version $Revision: 466595 $ $Date: 2006-10-21 23:24:41 +0100 (Sat, 21 Oct 2006) $
 */

public interface Pipeline {


    // ------------------------------------------------------------- Properties


    /**
     * <p>Return the Valve instance that has been distinguished as the basic
     * Valve for this Pipeline (if any).
     */
    //获取基础阀
    public Valve getBasic();


    /**
     * <p>Set the Valve instance that has been distinguished as the basic
     * Valve for this Pipeline (if any).  Prioer to setting the basic Valve,
     * the Valve's <code>setContainer()</code> will be called, if it
     * implements <code>Contained</code>, with the owning Container as an
     * argument.  The method may throw an <code>IllegalArgumentException</code>
     * if this Valve chooses not to be associated with this Container, or
     * <code>IllegalStateException</code> if it is already associated with
     * a different Container.</p>
     *
     * @param valve Valve to be distinguished as the basic Valve
     */
    //设置基础阀
    public void setBasic(Valve valve);


    // --------------------------------------------------------- Public Methods


    /**
     * <p>Add a new Valve to the end of the pipeline associated with this
     * Container.  Prior to adding the Valve, the Valve's
     * <code>setContainer()</code> method will be called, if it implements
     * <code>Contained</code>, with the owning Container as an argument.
     * The method may throw an
     * <code>IllegalArgumentException</code> if this Valve chooses not to
     * be associated with this Container, or <code>IllegalStateException</code>
     * if it is already associated with a different Container.</p>
     *
     * @param valve Valve to be added
     *
     * @exception IllegalArgumentException if this Container refused to
     *  accept the specified Valve
     * @exception IllegalArgumentException if the specifie Valve refuses to be
     *  associated with this Container
     * @exception IllegalStateException if the specified Valve is already
     *  associated with a different Container
     */
    //设置阀
    public void addValve(Valve valve);


    /**
     * Return the set of Valves in the pipeline associated with this
     * Container, including the basic Valve (if any).  If there are no
     * such Valves, a zero-length array is returned.
     */
    //获取阀
    public Valve[] getValves();


    /**
     * Cause the specified request and response to be processed by the Valves
     * associated with this pipeline, until one of these valves causes the
     * response to be created and returned.  The implementation must ensure
     * that multiple simultaneous requests (on different threads) can be
     * processed through the same Pipeline without interfering with each
     * other's control flow.
     *
     * @param request The servlet request we are processing
     * @param response The servlet response we are creating
     *
     * @exception IOException if an input/output error occurs
     * @exception ServletException if a servlet exception is thrown
     */
    //执行invoke方法
    public void invoke(Request request, Response response)
        throws IOException, ServletException;


    /**
     * Remove the specified Valve from the pipeline associated with this
     * Container, if it is found; otherwise, do nothing.  If the Valve is
     * found and removed, the Valve's <code>setContainer(null)</code> method
     * will be called if it implements <code>Contained</code>.
     *
     * @param valve Valve to be removed
     */
    //移除阀
    public void removeValve(Valve valve);
}

前面我们说过,当连接器调用容器的invoke()方法后,容器中要执行的任务并没有硬编码写在invoke()方法中,而是容器会去调用其管道的invoke()方法。StandardPipeline类是实现了Pipline接口。看看StandardPipeline中的invoke()方法
package org.apache.catalina.core.StandardPipeline;
   /**
     * Cause the specified request and response to be processed by the Valves
     * associated with this pipeline, until one of these valves causes the
     * response to be created and returned.  The implementation must ensure
     * that multiple simultaneous requests (on different threads) can be
     * processed through the same Pipeline without interfering with each
     * other's control flow.
     *
     * @param request The servlet request we are processing
     * @param response The servlet response we are creating
     *
     * @exception IOException if an input/output error occurs
     * @exception ServletException if a servlet exception is thrown
     */
    public void invoke(Request request, Response response)
        throws IOException, ServletException {

        // Invoke the first Valve in this pipeline for this request
        (new StandardPipelineValveContext()).invokeNext(request, response);

    }
StandardPipelineValveContext类是StandardPipeline类的一个内部类,一旦调用了StandardPipeline类中的invoke()方法时,它会把工作移交给StandardPipelineValveContext()方法。看看这个内部类的实现:
 // ------------------------------- StandardPipelineValveContext Inner Class


    protected class StandardPipelineValveContext
        implements ValveContext {


        // ------------------------------------------------- Instance Variables


        protected int stage = 0;


        // --------------------------------------------------------- Properties


        /**
          * Return descriptive information about this ValveContext
          * implementation.
          */
        public String getInfo() {
            return info;
        }


        // ----------------------------------------------------- Public Methods


        /**
         * Cause the <code>invoke()</code> method of the next Valve that is
         * part of the Pipeline currently being processed (if any) to be
         * executed, passing on the specified request and response objects
         * plus this <code>ValveContext</code> instance.  Exceptions thrown by
         * a subsequently executed Valve (or a Filter or Servlet at the
         * application level) will be passed on to our caller.
         *
         * If there are no more Valves to be executed, an appropriate
         * ServletException will be thrown by this ValveContext.
         *
         * @param request The request currently being processed
         * @param response The response currently being created
         *
         * @exception IOException if thrown by a subsequent Valve, Filter, or
         *  Servlet
         * @exception ServletException if thrown by a subsequent Valve, Filter,
         *  or Servlet
         * @exception ServletException if there are no further Valves
         *  configured in the Pipeline currently being processed
         */
        public void invokeNext(Request request, Response response)
            throws IOException, ServletException {

            int subscript = stage;
            stage = stage + 1;

            // Invoke the requested Valve for the current request thread
            if (subscript < valves.length) {
                valves[subscript].invoke(request, response, this);//调用管道中的每个阀,这里把内部类实例也传进去,方便调用其它阀
            } else if ((subscript == valves.length) && (basic != null)) {
                basic.invoke(request, response, this);//调用基础阀
            } else {
                throw new ServletException
                    (sm.getString("standardPipeline.noValve"));
            }

        }
    }
invokeNext()方法使用变量subscript和stage标明当前正在调用的阀。当第一次调用管道的invoke()方法,subscript的值为0,stage的值为1,因此,第1个阀(数组索引为0)会被调用。管道中的第1个阀接收ValueContext实例,并调用其invokeNext()方法。这时,subscript的值变为1,这样就会调用第2个阀,以后以此类推。
下来来看看几个重要的接口:
1,Value接口:
阀是Value接口的实例,用来处理接收到的请求,该接口有两个方法,invoke()方法和getInfo()方法。getInfo()方法返回阀的实现信息。
    public String getInfo();
    public void invokeNext(Request request, Response response)
        throws IOException, ServletException;

2,ValueContext接口:
这个接口是内部类StandardPipelineValveContex需要实现的接口,具有的方法跟Value接口一样。
 //-------------------------------------------------------------- Properties

    public String getInfo();
    public void invokeNext(Request request, Response response)
        throws IOException, ServletException;
3,Contained接口:
阀可以选择是否实现org.apache.catalina.Contained接口。该接口的实现类可以通过接口中的方法至多与一个servlet容器相关联。
//-------------------------------------------------------------- Properties

    /**
     * Return the <code>Container</code> with which this instance is associated
     * (if any); otherwise return <code>null</code>.
     */
    public Container getContainer();

    /**
     * Set the <code>Container</code> with which this instance is associated.
     *
     * @param container The Container instance with which this instance is to
     *  be associated, or <code>null</code> to disassociate this instance
     *  from any Container
     */
    public void setContainer(Container container);
4,Wrapper接口:
Wrapper级的servlet容器是一个org.apache.catalina.Wrapper接口的实例,表示一个独立的serlvet定义。Wrapper接口继承自Container接口,又添加了一些额外的方法。Wrapper接口的实现类要负责管理servlet类的servlet生命周期,即,调用servelet的init(),service(),destory()等方法,由于Wrapper已经是最低级的servlet容器了,因此不能再向其中添加子容器了,若是Wrapper的addChild()方法被调用,会抛出IllegalArgumantException异常。
Wrapper接口有两个比较重要的方法load()和allocate()方法。allocate()方法会分配一个已经初始化的servlet实例,而且allocate()方法还要考虑下该servlet类是否实现了javax.servlet.SingleThreadModel接口。load()方法载入并初始化servlet类。
public javax.serlvet.servlet allocate()throws javax.servlet.servletException;
public void load()throws javax.servlet.servletException
这两个方法暂时不是这里的重点,具体到类装载器会仔细讨论它。
那么现在来看看是如何调用过滤连中的阀的。请先看下面自己画的一张类图:


注意这一节的调度关系,从Container的invoke------Pipeline的invoke---------StandardPipelineValueContext的invokeNext-----------再到各个Vaule的invoke

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值