方法调用序列

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



 这个是前面第五章里,我画的图:



 我们再回顾一下自从连接器里

 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 StandardPipelineValveContext()).invokeNext(request, response);

    }

4StandardPipeline有个内部类StandardPipelineValveContext,它的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)的程序组件。

一个SingleThreadModel组件,可以保证在任何时候,只有一个线程来访问自己。

很多程序员以为,只要实现了上述接口就能保证自己的servlet是线程安全的。

其实不然,因为尽管stm可以保证一个servlet可以只被一个线程访问,但因为效率的关系,对于stm,tomcat会设置一个servletpool,里面会放置多个servlet(都是某一个servlet的实例,当然一个 StanderWrapper也就管一个servlet类)那么这个时候就会出问题了,例如如果若干个servlet的service方法都访问某个静态类的变量或servelt类以外的类或变量呢?

相关代码

public Servlet allocate() throws ServletException {

        // If not SingleThreadedModel, return the same instance every time
        if (!singleThreadModel) {

            // Load and initialize our instance if necessary
            if (instance == null) {
                synchronized (this) {
                    if (instance == null)
                            instance = loadServlet();
                }
            }
            if (!singleThreadModel) {

                countAllocated++;
                return (instance);
            }
        }

        synchronized (instancePool) {          //能运行到这里,说明一定是实现了singleThreadModel

            while (countAllocated >= nInstances) {       //会给池中不断地放置servlet
                if (nInstances < maxInstances) {
                        instancePool.push(loadServlet());
                        nInstances++;

                } else {
                        instancePool.wait();
                }
            }
            countAllocated++;
            return (Servlet) instancePool.pop();  //最后返回顶上的一个

        }

    }

当然当servlet消亡的时候,还会回到池中

 public void deallocate(Servlet servlet) throws ServletException {

        // If not SingleThreadModel, no action is required
        if (!singleThreadModel) {
            countAllocated--;
            return;
        }

        // Unlock and free this instance
        synchronized (instancePool) {
            countAllocated--;
            instancePool.push(servlet);
            instancePool.notify();
        }

    }

因而这个接口在Servlet 2.4中就已经废弃了,就是因为它让程序员产生了虚假的安全感。

StandardWrapper

StandardWrapper的主要作用就是载入它自己所代表的servlet类,并进行实例化,但是通过分析上面的调用时序图,大家知道它是先调用自己的管道,然后是基础阀,由基础阀调用StandardWrapper的alloacte方法。

上面已经说了,有个STM,如果容器只是维护一个实现了STM接口的servelt那么调用的时候就应该是这样的

    Servlet instance = <get an instance of the servlet>;
    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里我们就已经指定了WebappLoader,通过它,我们可以获得WebappClassLoader这个对象。

不过在tomcat中,如果要加载的servlet位于org.apache.catalina.下,那么classLoader就是

        classLoader = this.getClass().getClassLoader(); //this 就是StandardWrapper

再下来就是loadClass,接着用Class的newInstance获得servlet;

下来就是检查该servlet是否允许载入(这步我看的不是很懂),看它是否实现了ContainerServlet接口。

ContainerServlet接口中有set/getWrapper方法,就是可以让servlet访问Catalina的内部功能。

下面就是调用 servlet.init(facade);这个facade是javax.servlet.ServletConfig的一个外观变量。

如果该StandardWrapper对象表示的是一个STM Servlet,将该实例添加到实例池中,因此,如果实例池如果为null,首先需要创建它。

   // Register our newly initialized instance

   singleThreadModel = servlet instanceof SingleThreadModel;

   if (singleThreadModel) {

    if (instancePool == null)

        instancePool = new Stack();

    }

    fireContainerEvent("load", this);

    }

 最后返回servlet。

ServletConfig对象

上面servlet的init方法的参数实际上就是javax.servlet.ServletConfig接口的实例。

 问题出现了,这个接口的实例从哪来来呢?大家看看StandardWrapper的声明部分,就知道它本身就实现了ServletConfig接口。

 但是在调用init方法是,StandardWrapper并不会直接把自己传过去而是使用了一个facade,为什么我主要是直接把StandardWrapper传过去,那么StandardWrapper里面的所有public方法不都暴露了么?

 ServletConfig 接口有以下四个方法getServletContext,getServletName,getInitParameter,和getInitParameterNames。



1 getServletContext

     public ServletContext getServletContext() {

        if (parent == null)

            return (null);

        else if (!(parent instanceof Context))

            return (null);

        else

            return (((Context) parent).getServletContext());

    }

现在你知道不能单独部署一个包装器来表示一个 Servlet,包装器必须从属于一个上下文容器,这样才能使用 ServletConfig 对象使用getServletContext 方法获得一个 ServletContext 实例。



2 getServletName

  获得servlet的名字,没有什么好说的



3 getInitParameter

  获得指定的初始参数的值。StandardWrapper中的初始参数放在一个HashMap中:

  private HashMap<String, String> parameters = new HashMap<String, String>();

  具体的实现大家看代码,这块很简单。



4 getInitParameterNames

  返回的是初始化参数的名字的集合,是一个枚举类。

StandardWrapperFacade类

类图如下:

ServletConfig共有四个方法,facade类getServletName,getInitParameter,getInitParameterNames都是直接调用StandardWrapper,这些都比较简单,没有什么要说的。

不过getServletContext就有点复杂了:

    public ServletContext getServletContext() {

        ServletContext theContext = config.getServletContext();

        if ((theContext != null) &&

            (theContext instanceof ApplicationContext))

            theContext = ((ApplicationContext) theContext).getFacade();

        return (theContext);

    }

看到了吧,先调用StandardWrapper获得Context,但是这里最后给外面返回的还是Facade。(真tm复杂)。



下面的章节我们会讲

StandardWrapperValve,FilterDef,ApplicationFilterConfig,ApplicationFilterChain

how tomcat works 读书笔记 十一 StandWrapper 上的更多相关文章

  1. how tomcat works 读书笔记 十一 StandWrapper 下

    StandardWrapperValve StandardWrapperValve是StandardWrapper的基础阀,主要完成了三个工作. 1 调用StandardWrapper的allocat ...

  2. how tomcat works 读书笔记(二)----------一个简单的servlet容器

    app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/arti ...

  3. how tomcat works 读书笔记四 tomcat的默认连接器

    事实上在第三章,就已经有了连接器的样子了,只是那仅仅是一个学习工具,在这一章我们会開始分析tomcat4里面的默认连接器. 连接器 Tomcat连接器必须满足下面几个要求 1 实现org.apache ...

  4. How tomcat works 读书笔记十七 启动tomcat 上

    一路跋山涉水,这是最后一章了. 关于tomcat的启动,有两个类,一个是Catalina类,一个是Bootstrap类. 理论上,两个类可以和到一起,但是为了支持多种运行模式,又把他们分开了. 为了让 ...

  5. How tomcat works 读书笔记十五 Digester库 上

    Digester库 在前面的几个章节里,我们对tomcat里各个组件的配置完全是使用写硬编码的形式完成的. 如 Context context = new StandardContext(); Loa ...

  6. How Tomcat Works 读书笔记 八 载入器 上

    Java的类载入器 详细资料见 http://blog.csdn.net/dlf123321/article/details/39957175 http://blog.csdn.net/dlf1233 ...

  7. How tomcat works 读书笔记十二 StandardContext 上

    在tomcat4中,StandardContext.java是最大的一个类,有117k.废话不说,开始分析吧. 其实要分析StandardContext,也就主要分析两个方法,一个start,一个in ...

  8. How Tomcat Works 读书笔记 八 加载器 上

    Java的类加载器 具体资料见 http://blog.csdn.net/dlf123321/article/details/39957175 http://blog.csdn.net/dlf1233 ...

  9. How tomcat works 读书笔记十五 Digester库 下

    在这一节里我们说说ContextConfig这个类. 这个类在很早的时候我们就已经使用了(之前那个叫SimpleContextConfig),但是在之前它干的事情都很简单,就是吧context里的co ...

随机推荐

  1. Microsoft Dynamics CRM 2013/2015 选项集的多选

    CRM中的选项集多选一直是客户需求中的必选项,但从CRM进国内的3.0时代开始到目前的2015版本均没有提供该功能,但既然客户要了就得想办法满足,既然CRM本身的功能上不支持,那我们只有使用非官方支持 ...

  2. FFmpeg源代码简单分析:avformat_close_input()

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  3. JQuery实战---窗口效果

    在前面的相关博文中,小编对jquery的相关知识进行了简单的总结,关于jquery的很多小的知识点,都需要我们自己去动手和实践,一行行代码都需要我们自己亲自动手去敲,今天我们继续来学习jquery的相 ...

  4. Android Studio科普篇——2.误区

    1.删除一行是ctrl+x? 这是一个被谣传得很广泛的快捷键,但其实删除一行的快捷键不是ctrl+x,而是ctrl+y.ctrl+x在不选中内容的情况下,是剪切当前行,而ctrl+y才是删除当前行,它 ...

  5. Spring事务管理与数据库隔离级别的关系(Spring+mysql)

    之前写过一篇文章<数据库隔离级别(mysql+Spring)与性能分析 >,里面很多问题写的不是很专业,也不是很有逻辑性,现在重新整理一下,希望对大家有帮助. 这部分通过两天时间反复的 ...

  6. 【一天一道LeetCode】#121. Best Time to Buy and Sell Stock

    # 一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Say ...

  7. Windows Server2012R2 安装 SharePoint 2013 的必备组件

    Windows Server2012R2目前支持SharePoint Server 2013 with Service Pack 1 和 SharePoint Foundation 2013 with ...

  8. JAVA内部类_1

    使用内部类的原因: (1)可以访问该类定义所在作用域中的数据,包括私有数据. (2)可以对同一个包中的其它类隐藏起来. (3)当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷. 下面 ...

  9. 清除行内元素之间的HTML空白

    原文连接:Remove Whitespace Between Inline-Block Elements 原文日期: 2013年8月27日 翻译日期: 2013年8月28日 至今我还记得年轻是在IE6 ...

  10. Chapter 2 User Authentication, Authorization, and Security(5):使用固定服务器角色

    原文出处:http://blog.csdn.net/dba_huangzj/article/details/38844999,专题目录:http://blog.csdn.net/dba_huangzj ...