how tomcat works 读书笔记 十一 StandWrapper 上(一)

2015-01-27 06:20:26 · 作者: · 浏览: 25

方法调用序列

下图展示了方法调用的协作图:

\
这个是前面第五章里,我画的图:
\
我们再回顾一下自从连接器里
connector.getContainer().invoke(request, response);
这句代码运行之后发生的事情;
这是第五章的时序图,放在这一章同样适用。。。
我们仔细分析一下:
1首先连接器创建请求与响应对象;
2调用这行代码
connector.getContainer().invoke(request, response)
(我们一StandContext为顶层容器)
3在容器里会先调用StandardPipeline的invoke方法。如下:
   public void invoke(Request request, Response response)
        throws IOException, ServletException {

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

    }
4StandardPipeline有个内部类StandardPipelineva lveContext,它的invokeNext方法会循环管道(StandardContext的管道)里的所有阀,直到基础阀,并且调用其invoke方法。基础阀在StandardContext初始化的时候就默认指定了StandardContextValve();
5基础阀的invoke方法调用StandardContext的map方法得到Wrapper
6调用得到的wrapper(这里是StandardWrapper)的invoke方法...
7先重复上面的第三第四步,这时获得的基础阀是StandardWrapperValve,它的invoke方法就是调用StandardWrapper类的allocate方法,allocate调用loadServlet获得servlet。
8产生一个在ApplicationFilterChain:createFilterChain(request, servlet),然后在其internalDoFilter方法中调用servlet的service方法。

SingleThreadModel接口

这个接口能保证,在容器内部,任何时候只有一个进程访问某个serlvet的service方法。
这个接口是一个空接口,或者叫标志接口,内部什么都没有。就是一种标示而已。
实现此接口的一个servlet通俗称为SingleThreadModel(STM)的程序组件。
很多程序员以为,只要实现了上述接口就能保证自己的servlet是线程安全的。
其实不然,例如如果若干个servlet的service方法都访问某个静态类的变量或servelt类以外的类或变量呢?
因而这个接口在Servlet 2.4中就已经废弃了,就是因为它让程序员产生了虚假的安全感。

StandardWrapper

StandardWrapper的主要作用就是载入它自己所代表的servlet类,并进行实例化,但是通过分析上面的调用时序图,大家知道它是先调用自己的管道,然后是基础阀,由基础阀调用StandardWrapper的alloacte方法。
上面已经说了,有个STM,如果容器只是维护一个实现了STM接口的servelt那么调用的时候就应该是这样的
    Servlet instance = 
  
   ;
    if ((servlet implementing SingleThreadModel>) {
        synchronized (instance) {
            instance.service(request, response);
        }
    }
    else {
        instance.service(request, response);
    }
  
不过为了保持性能,StandardWrapper一般维护了一个STM servlet 实例池。
一个包装器还负责准备一个 javax.servlet.ServletConfig 的实例,这可以在
servlet 内部完成,接下来两小节讨论如何分配和加载 servlet。


Alloacte方法

本方法其实可以分为两部分:
第一 产生非STMServlet
StandardWrapper 定义一个 java.servlet.Servlet 类型的实例
private Servlet instance = null;
方法 allocate 检查该实例是否为 null,如果是调用 loadServlet 方法来加载servlet。然后增加 contAllocated 整型并返回该实例。

第二 产生STMServlet
方法 allocate 尝试返回池中的一个实例,变量intancePool是一个java.util.Stack类型的 STM servlet实例池。
在这里我需要给大家解释三个变量:
countAllocated: 目前存活的servelt数量
the count of allocations that are currently active (even if they are for the same instance, as will be true on a non-STM servlet).
nInstances : 已经加载的serlvet数量
Number of instances currently loaded for a STM servlet.
maxInstances: 这个看名字就知道了,StandardWrapper支持的最大数目的servlet。
上面三个变量肯定把大家搞晕了,但是我想说记着一个StandardWrapper就维护一个servlet,它只有一个class地址,那三个变量是在多线程的时候用的!
这部分的代码比较麻烦,不管是理解还是说都很麻烦
总而言之,最开始的时候countAllocated与nInstances都是0,先loadServlet()出一个servelt,push到servlet实例池里面,然后取出来..
这部分大家自己看代码吧(我觉得自己偷的一把好懒呀)

loadServlet方法

这个方法首先会回检查instance,如果不为null,并且当前的StandardWrapper并不表示一个STMServlet类,那么就直接返回。
另一方面,Catalina也是 jsp容器,如果请求的servelt是一个jsp就执行下面的代码段:
String actualClass = servletClass;
if ((actualClass == null) && (jspFile != null)) {
Wrapper jspWrapper = (Wrapper)((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
if (jspWrapper != null)
actualClass = jspWrapper.getServletClass();
}
下面就是获得classloader,默认情况下,我们看前面几章的内容就知道,在Bootstrap里我们就已经指定了WebappLoad