• Tomcat学习笔记(十)


      

      StandardWrapper容器

      Context容器包含一个或者多个Wrapper实例,每个Wrapper实例表示一个具体的servlet定义。 

           方法调用序列 

            

      

      具体过程 

      (1)连接器创建request和response对象 
      (2)连接器调用StandardContext实例的invoke()方法 
      (3)接着,StandardContext实例的invoke方法调用其管道对象的invoke方法。StandardContext中管道对象的基础阀是StandContextValve类的实例,因此,StandardContext的管道对象会调用StandardContextValve实例的invoke方法。 
      (4)StandardContextValve实例的invoke方法获取相应的Wrapper实例处理HTTP请求,调用Wrapper实例的invoke的方法。 
      (5)StandardWrapper类是Wrapper接口的标准实现,StandardWrapper实例的invoke方法会调用其管道对象的invoke方法。 
      (6)StandardWrapper流水线的基本阀门时StandardWrapperValve。因此StandardWrapperValve的invoke方法会被调用。StandardWrapperValve的invoke方法会调用包装器的allocate方法获得一个servlet的实例。 
      (7)当一个servlet需要被加载的时候,方法allocate调用方法load来加载一个servlet。 
      (8)方法load会调用servlet的init方法。 
      (9)StandardWrapperValve调用servlet实例的service方法。

         1. StandardWrapper对象主要任务加载servlet类,并进行实例。但是StandardWrapper并不调用servlet的service方法。StandardWrapperValve对象通过调用allocate()方法从StandardWrapper实例中获取servlet实例。在获取得了servlet实例后,StandardWrapperValve实例就会调用servlet实例的service方法。 
    粗略流程如下: 

                             

      分配Servlet实例 
      StandardWrapperValve实例的invoke方法调用Wrapper实例的allocate方法获取请求的servlet的一个实例,因此StandardWrapper类的实现allocate方法。 
    StandardWrapperValve#invoke

     public final void invoke(Request request, Response response)
            throws IOException, ServletException {
            //...省略
            // Allocate a servlet instance to process this request
            try {
                if (!unavailable) {
                    servlet = wrapper.allocate();
                }
            } catch (UnavailableException e) {
            //...省略

    StandardWrapper#allocate

     /**
      * Stack containing the STM instances.
      */
    protected Stack<Servlet> instancePool = null;
    
     public Servlet allocate() throws ServletException {
    
            // If we are currently unloading this servlet, throw an exception
            if (unloading)
                throw new ServletException
                  (sm.getString("standardWrapper.unloading", getName()));
    
            boolean newInstance = false;
    
            // 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 {
                                if (log.isDebugEnabled())
                                    log.debug("Allocating non-STM instance");
    
                                instance = loadServlet();//加载servlet实例
                                if (!singleThreadModel) {
                                    // For non-STM, increment here to prevent a race
                                    // condition with unload. Bug 43683, test case
                                    // #3
                                    newInstance = true;
                                    //分配计数
                                    countAllocated.incrementAndGet();
                                }
                            } catch (ServletException e) {
                                throw e;
                            } catch (Throwable e) {
                                ExceptionUtils.handleThrowable(e);
                                throw new ServletException
                                    (sm.getString("standardWrapper.allocate"), e);
                            }
                        }
                    }
                }
    
                if (!instanceInitialized) {
                    initServlet(instance);//初始化servlet->servlet.init()
                }
    
                if (singleThreadModel) {
                    if (newInstance) {
                        // Have to do this outside of the sync above to prevent a
                        // possible deadlock
                        synchronized (instancePool) {
                            instancePool.push(instance);
                            nInstances++;
                        }
                    }
                } else {
                    if (log.isTraceEnabled())
                        log.trace("  Returning non-STM instance");
                    // For new instances, count will have been incremented at the
                    // time of creation
                    if (!newInstance) {
                        countAllocated.incrementAndGet();
                    }
                    return (instance);
                }
            }
    
            synchronized (instancePool) {
    
                while (countAllocated.get() >= 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) {
                            ExceptionUtils.handleThrowable(e);
                            throw new ServletException
                                (sm.getString("standardWrapper.allocate"), e);
                        }
                    } else {
                        try {
                            instancePool.wait();
                        } catch (InterruptedException e) {
                            // Ignore
                        }
                    }
                }
                if (log.isTraceEnabled())
                    log.trace("  Returning allocated STM instance");
                countAllocated.incrementAndGet();
                return instancePool.pop();//弹出栈顶servlet
    
            }
    
        }

    加载Servlet 
    在StandardWrapper#loadServlet()方法,先检查是否已经加载过servlet

    if (!singleThreadModel && (instance != null))
                return instance;

    StandardContext获取实例管理器,并通过实例管理器获取servlet实例

    InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
     servlet = (Servlet) instanceManager.newInstance(servletClass);

    初始化servlet,并触发容器加载监听事件。

    initServlet(servlet);
    fireContainerEvent("load", this);

    最后,返回servlet

    return servlet;

     2.ServletConfig类 
    在servlet调用init方法,要传入ServletConfig参数,ServletConfig参数的来源,由于StandardWrapper不仅实现了Wrapper接口,还实现了javax.servlet.ServletConfig接口,在ServletConfig接口中的方法如下:

    public interface ServletConfig {
        public String getServletName();//返回servlet名字
        public ServletContext getServletContext();//返回servlet容器
        public String getInitParameter(String name);//返回初始化参数
        public Enumeration<String> getInitParameterNames();
    }

    StandardWrapper类并不是将自身传递给servlet#init方法,它会在一个StandardWrapperFacade实例中包装自生,将其大部分的公共方法对servlet程序员隐藏。

    /**
         * The facade associated with this wrapper.
         */
    protected StandardWrapperFacade facade = new StandardWrapperFacade(this);

    在StandardWrapper中方法实现

    public ServletContext getServletContext() {
            if (parent == null)
                return (null);
            else if (!(parent instanceof Context))
                return (null);
            else
                return (((Context) parent).getServletContext());
        }
    
    protected HashMap<String, String> parameters = new HashMap<String, String>();
        /**
         * Return the name of this servlet.
         */
        @Override
        public String getServletName() {
            return (getName());
        }
         public String getInitParameter(String name) {
            return (findInitParameter(name));
        }
        /**
         * Return the set of initialization parameter names defined for this
         * servlet.  If none are defined, an empty Enumeration is returned.
         */
        @Override
        public Enumeration<String> getInitParameterNames() {
            try {
                parametersLock.readLock().lock();
                return Collections.enumeration(parameters.keySet());
            } finally {
                parametersLock.readLock().unlock();
            }
        }
        //初始化参数
         public void addInitParameter(String name, String value) {
            try {
                parametersLock.writeLock().lock();
                parameters.put(name, value);
            } finally {
                parametersLock.writeLock().unlock();
            }
            fireContainerEvent("addInitParameter", name);
        }

    3.Servlet容器的父子关系。 
    Wrapper实例代表一个servlet实例,因此Wrapper实例不在有子容器,不应该在调用其addChild方法,否则抛出IllegalStateException异常。下面是addChild方法。

    public void addChild(Container child) {
            throw new IllegalStateException
                (sm.getString("standardWrapper.notChild"));
        }

    Wrapper的父容器只能是Context类的实现,若是在调用Wrapper实例的setParent方法时,传入了一个非Context类型的容器,则会抛出IllegalStateException异常。

    public void setParent(Container container) {
    
            if ((container != null) &&
                !(container instanceof Context))
                throw new IllegalArgumentException
                    (sm.getString("standardWrapper.notContext"));
            if (container instanceof StandardContext) {
                swallowOutput = ((StandardContext)container).getSwallowOutput();
                unloadDelay = ((StandardContext)container).getUnloadDelay();
            }
            super.setParent(container);
    
        }

    4.StandardWrapperFacade类 
    StandardWrapper实例会调用它所载入的servlet类的实例的init()方法。init()方法需要一个javax.servlet.ServletConfig实例,而StandardWrapper类本身实现了javax.servlet.ServletConfig接口,所以,理论上StandardWrapper对象可以将自己传入init()方法。但是StandardWrapper需要将其大部分公共方法对servlet程序员隐藏起来。为了实现在这个目的,StandWrapper类将自身包装成StandardWrapperFacade类的一个实例。关系如下。

                                   

    (1)在StandardWrapper类中,将自身包装成StandardWrapperFacade。

    /**
     * The facade associated with this wrapper.
    */
    protected StandardWrapperFacade facade = new StandardWrapperFacade(this);

    (2)StandardWrapperFacade中的构造方法。

    public StandardWrapperFacade(StandardWrapper config) {
            super();
            this.config = config;
        }
     //通过包装类型调用方法
    public String getInitParameter(String name) {
            return config.getInitParameter(name);
        }
    public ServletContext getServletContext() {
            if (context == null) {
                context = config.getServletContext();
                if ((context != null) && (context instanceof ApplicationContext))
                    context = ((ApplicationContext) context).getFacade();
            }
            return (context);
        }
    public String getServletName() {
            return config.getServletName();
        }  

    这里采用装饰模式。 
    5.StandardWrapperValve类 
    StandardWrapperValve是StandardWrapper中的基础阀门,主要完成2个操作。 
    (1)执行与该servlet实例关联的全部过滤器, 
    (2)调用servlet实例的service方法。 
    在StandardWrapperValve中invoke方法执行的步骤。 
    (1)调用StandardWrapper实例的allocate方法获取该StandardWrapper实例所表示的servlet的实例; 
    (2)调用私有方法createFilterChain,创建过滤器链; 
    (3)调用过滤链的doFilter方法,其中包括调用servlet实例的service方法; 
    (4)释放过滤链; 
    (5)调用Wrapper实例的deallocate方法; 
    (6)若该servlet类再也不会用到,将会调用Wrapper实例的unload方法。 
    6.ApplicationFilterChain类 
    ApplicationFilterChain类实现了FilterChain接口,StandardWrapperValve类的invoke方法会创建ApplicationFilterChain类的一个实例,并调用其doFilter方法,doFilter方法会调用第一个过滤器的doFilter方法。直到最后一个过滤器,则会调用被请求的servlet类的service方法。

    总结:

      通过StandWrapper的学习,大致的了解了Servlet的从加载,初始化,service方法,到卸载的整个流程。另外还有就是单例模式,监听事件,装饰模式的设计思想的了解。

  • 相关阅读:
    Go语言中Path包用法
    golang读取文件
    golang 判断文件或文件夹是否存在
    一个好玩的 屏蔽别人审查元素F12 右键
    view Table组件报错 You may have an infinite update loop in watcher with expression "columns"
    边框渐变色
    eclipse采用export方式打war包,结果里面的文件不一致
    jqgrid和vue结合的问题
    关于socket通信bind()返回值错误:10049
    Mybatis in 查询的实现
  • 原文地址:https://www.cnblogs.com/lzeffort/p/7107655.html
Copyright © 2020-2023  润新知