Tomcat从零开始(十七)——StandardWrapper

系统 1637 0

第十七课: StandardWrapper

课前复习:

       不知道大家是否还有印象,就是在 6 7 节课说的 4 container, 粗略的从大到小来说就是 engine,host,context , wrapper 。当时写的时候很纠结,因为后面有详细介绍这 4 个的计划,所以前面写的可能不是很详尽。

       让我们回忆一下,当一个请求到来的时候,发生了什么。比如什么创建 Request 这里就不说了,之后 connector 会调用与之关联的容器的 invoke 方法,之后那就肯定会调用 pipeline invoke ,之后一顿 invoke valve 。好,那让我们回想一下之前写过的 context wrapper ,总结一个比较详细的执行过程。

1.        Connector 创建 req resp

2.        调用 StandardContext invoke ,调用 xxxPipeline invoke 方法

3.        Pipeline 调用了 wrapper invoke 方法

4.        Wrapper 调用 valve invoke 方法

5.        valve 调用了 servlet allocate (这里在以前的课程中讲过)

6.        allocate 方法调用 servlet load 方法 ( servlet 需要加载的时候 )

7.        init 方法,之后就是 servlet 处理了。

关于 SingleThreadModel

       这个 SingleThreadModel servlet2.4 以上版本就已经移除了,因为这个东西只能给你的 servlet service 保证同一时刻只有一个进程在访问,给人一种假象的安全。而且只是给 service 方法给予同步,这显然是不能完全解决多线程访问的问题的。其实说这个是为了给下面的 StandardWrapper 做铺垫。因为我们都知道 StrandardWrapper 是负责加载它所代表的 servlet allocate 一个对象的实例。之后交给 valve 来调用 servlet service 方法。这个 allocate 在使用 不使用 SingleThreadModel 的时候是不同的。好的,我们接下来就说这个 StandardWrapper

StandardWrapper

       这个之前介绍过了,我们这次主要介绍的是 allocate 方法, 我们看下面这一段源码可以发现,当没有实现 SingleThreadModel 的时候, allocate 总是返回第一次时候产生的 servlet 实例。而如果实现 SingleThreadModel 接口,那么就开始控制分配的数量,当分配的大于 nInstance 时候,就 load 一个 servlet 实例,当然这个 load 实在 maxInstance 控制之内的。

       代码如下。

 

          public Servlet allocate() throws ServletException {

        if (debug >= 1)
            log("Allocating an instance");

        // If we are currently unloading this servlet, throw an exception
        if (unloading)
            throw new ServletException
              (sm.getString("standardWrapper.unloading", getName()));

        // 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) {
                        try {
                            instance = loadServlet();
                        } catch (ServletException e) {
                            throw e;
                        } catch (Throwable e) {
                            throw new ServletException
                                (sm.getString("standardWrapper.allocate"), e);
                        }
                    }
                }
            }

            if (!singleThreadModel) {
                if (debug >= 2)
                    log("  Returning non-STM instance");
                countAllocated++;
                return (instance);
            }

        }

        synchronized (instancePool) {

            while (countAllocated >= nInstances) {
                // Allocate a new instance if possible, or else wait
                if (nInstances < maxInstances) {
                    try {
                        instancePool.push(loadServlet());
                        nInstances++;
                    } catch (ServletException e) {
                        throw e;
                    } catch (Throwable e) {
                        throw new ServletException
                            (sm.getString("standardWrapper.allocate"), e);
                    }
                } else {
                    try {
                        instancePool.wait();
                    } catch (InterruptedException e) {
                        ;
                    }
                }
            }
            if (debug >= 2)
                log("  Returning allocated STM instance");
            countAllocated++;
            return (Servlet) instancePool.pop();

        }

    }
    

 

Load

       这个没什么多说的,以前说过了,我们知道 wrapper 接口有一个 load 方法,其实他和 Allocate 里的一样,都是调用的 LoadServlet 方法。我们来看看它的源码。

    先看看这一段。

 

              // 这里是说如果不是第一次访问了,并且不是singleThreadModel
    	//就直接返回  Servlet实例
        if (!singleThreadModel && (instance != null))
            return instance;

	if ((actualClass == null) && (jspFile != null)) {
                Wrapper jspWrapper = (Wrapper)
                    ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
                if (jspWrapper != null) {
                    actualClass = jspWrapper.getServletClass();
                    // Merge init parameters
                    String paramNames[] = jspWrapper.findInitParameters();
                    for (int i = 0; i < paramNames.length; i++) {
                        if (parameters.get(paramNames[i]) == null) {
                            parameters.put
                                (paramNames[i], 
                            jspWrapper.findInitParameter(paramNames[i]));
                        }
                    }
                }
}

    
     这是Tomcat4 的方法,以后的版本就没有了。都是直接loadservletclass。之后就是Loader了,回忆一下我们当时讲的,是自定义的一个classLoader,需要注意的是,container提供一个特殊的servlet,可以访问container的内部内容,名称以org.apache.catalina.起始。之后就是加载servlet,之后就是权限验证,没啥说的。在之后就是在init()的前后fire事件。之后用instanceof来确定是否是实现了singleThreadModel的,如果是就放入pool(如果pool为空就创建一个新的)中。剩下就是Return了。

 



ServletConfig

       接下来就是 servletConfig ,这个东西我们回忆一下是在哪里看到这个东西的,想想在 servlet load 的时候,就会调用一个 init 方法,而且他的参数是 ServletConfig config ,那么,我们就需要知道 servlet 是怎么拿到 servletConfig 的,我们看源码,会发现 StandardWrapper 他实现了 ServletConfig 接口,所以说他把自己传过去就行了。但是想一个事情,如果这样可以,那么 servlet 的每一个实现就都可以用 wrapper 之内的方法了, wrapper container 的,所以我们要保证安全,那么就像 req,resp 那样,用一个 façade 设计模式就行,这个就是隐藏子系统。

 

Filter

       做过 web 开发,大家应该知道 filter 这个东西,那么 filter 是怎么实现的呢,我们想一下之前学的东西,我们应该能想到 ServletWrapperValve 做了这件事情,因为 filter 是跟 service 方法前后后关系。那么我们就可以知道 valve invoke 方法做了什么:

1.        首先要得到一个 wrapper 的对象, allocate 一个 servlet

2.        来一系列的 Filter ,调用他们的 doFilter 方法,当然还有 service 方法

3.        销毁,这个应该是在 servlet 超时的时候才进行。

剩下就是讲解一下 filter chain 了,这个东西其实是一个 ArrayList ,但是注意 filterChain 是一个对象,其中的 filter 是一个该对象之中的一个 arraylist, 所以我们就知道,这个东西其实就是靠 iterator 来实现一个接一个的调用过滤器。所以,本章就结束了,这章其实没有多少自己写的东西,基本都是对源代码的一种分析,希望大家可以多理解,或者下一个 tomcat sourcecode 版本,之后多用断点 跑一跑就能理解是怎么工作的了。

    最后还是冒充一下大神说说读代码的事吧,刚才有人问了问为啥我能读懂一些,刚开始的时候我也看不懂,其实就是你最开始看的时候不要纠结于所有的语句,至少你能看懂这个方法主要是干啥功能的,大概哪几个语句能实现就行了,等你慢慢的看完这个,再看完另一个,你就能知道到底那个语句是干什么的了。

 

 

Tomcat从零开始(十七)——StandardWrapper


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论