方法调用序列

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



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



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

 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. Google Dremel数据模型详解(下)

    "神秘"的r和d 单从数据结构来看的话,我们可以这样解释r和d的含义.r代表着当前字段与前一字段的关系,是在哪一层合并的,即公共的父结点在哪?举例来说,假如我们重建到了Code=' ...

  2. How to generate the complex data regularly to Ministry of Transport of P.R.C by DB Query Analyzer

    How to generate the complex data regularly to Ministry of Transport of P.R.C by DB Query Analyzer 1 ...

  3. 关于MT8127中sdk的编译出错问题

    今天在看MTK提供的SDK编译文档,按照步骤做,结果出错了,文档如下: 2- Building an SDK for MacOS and Linux ------------------------- ...

  4. 【一天一道LeetCode】#160. Intersection of Two Linked Lists

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

  5. Hessian源码分析--HessianProxy

    在上一篇博客 Hessian源码分析--HessianProxyFactory 中我们了解到,客户端获得的对象其实是HessianProxy生成的目标对象,当调用目标对象的方法时,会调用Hessian ...

  6. Gradle脚本打包so库

    要让引用的第三方的so库被打包进去,只需要把相关的armeabi文件夹放在libs下面,然后在builld.gradle脚本中加上这一句: sourceSets{ main { jniLibs.src ...

  7. FT5X06 如何应用在10寸电容屏(linux-3.5电容屏驱动简析&移植10寸电容屏驱动到Android4.2) (by liukun321咕唧咕唧)

    这是几个月以前的东西了,在彻底遗忘之前拿出来好好写写.做个笔记,也算是造福后来人了.在做这个项目之前,没有做过电容屏的驱动,印象中的电容触摸屏是不需要校正的.IC支持多大的屏就要配多大的屏.但是拿到需 ...

  8. 教你自己写Android第三方库

    其实Android studio的出现很大程度上方便了我们Android开发人员,今天我们说说怎么构建我们自己的库. 依次按File->New Moudle->android Librar ...

  9. 深入理解 JSX

    JSX 是一个看起来很像 XML 的 JavaScript 语法扩展.React 可以用来做简单的 JSX 句法转换. 为什么要使用 JSX? 你不需要为了 React 使用 JSX,可以直接使用纯粹 ...

  10. unity 快速创建小地图

    先写一个纹理遮罩shader Shader "Unlit/TexMask" { Properties { _MainTex ("Texture", 2D) =  ...