StandardWrapper

Posted 陈东

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了StandardWrapper相关的知识,希望对你有一定的参考价值。

Tomcat中有四种类型的Servlet容器,分别是 Engine、Host、Context、Wrapper,每个Wrapper实例表示一个具体的Servlet定义,StandardWrapper就是Catalina中的Wrapper接口的标准实现.

方法调用序列:指的是每当服务器接收到Http请求时,服务器中调用的一系列方法,对于每个引入的HTTP请求,连接器都会调用与其关联的Servlet容器的 invoke方法,然后,Servlet容器会调用其所有子容器的invoke方法,

具体的过程看下面:

  1. 连接器创建request和response对象;
  2. 连接器调用StandardContext实例的invoke方法,
  3. 接着StandardContext实例的invoke方法会调用其管道对象的额invoke方法,StandardContext对象的基础阀是StandardContextValue类的实例,因此StandardContext的管道对象会调用其基础阀的invoke方法,
  4. StandardContextValue实例的invoke方法会获取响应的Wrapper实例来处理HTTP请求,调用Wrapper实例的invoke方法
  5. StandardWrapper类是Wrapper接口的标准实现,StandardWrapper对象会调用其管道对象的invoke方法。
  6. StandardWrapper对象的基础阀是StandardWrapperValue类的实例,因此会调用StandardWrapperValue的invoke方法,其invoke方法会调用Wrapper实例的allocate方法获取servlet实例;
  7. allocate方法会调用load方法载入相应的servlet类,若已经载入,咋无需重复载入,
  8. load方法会调用servlet实例的init方法
  9. StandWrapperValue调用Servlet实例的service方法

注意:StandardContext和StandardWrapper两个类的构造函数都设置了响应的基础阀作为其基础阀,

SingleThreadModel

servlet类可以实现javax.servlet.SingleThreadModel接口,这样的Servlet类与被称为STM Servlet类,根据Servlet规范,实现此接口的目的是保证Servlet类实例一次只能处理一个请求,下面给出点Servlet2.规范中,SRV.14.2.21一节的内容

若Servlet类实现了SingleThreadModel接口,则可以保证绝不会有两个线程同时执行该Servlet实例的service方法,这一点由Servlet容器通过控制对单一Servlet实例的同步访问实现,或者维护一个Servlet实例池,然后将每个新请求分派给一个空闲的Servlet实例

该接口并不能方法之Servlet访问共享资源造成的同步问题,例如访问类的静态变量或访问Servlet作用域之外的类,

事实上 实现了SingleThreadModel接口的Servlet类只能保证在同一时刻,只有一个线程在执行该Servlet实例的service方法,但是为了提高执行性能,Servlet容器会创建多个同一(Servlet类)的STM Servlet实例,也就说,STM Servlet实例的service方法会在多个STM Servlet实例中并发的执行,如果Servlet实例需要访问静态类变量或类外的某些资源的话,就有可能引起同步问题。

多线程的虚假安全性

在Servlet 2.4 规范中,SingleThreadModel接口已经弃用了,因为他会使Servlet程序员误以为实现了该接口的Servlet类就是多线程安全的,但是Servlet2.3 和 2.4规范还对该接口提供了支持。

 

StandardWrapper

StandardWrapper对象主要任务是载入它多代表的servlet类,并进行实例化,但是,StandardWrapper类并不调用 Servlet类的service方法,该任务是由StandardWrapperValue对象(StandardWrapper实例管道对象的的基础阀)完成的,StandardWrapperValue对象通过调用与其关联的StandardWrpper类的allocate方法从StandardWrapper实例中获取Servlet实例,在获得了Servlet实例之后,StandardWrapperValue实例就会调用 Servlet实例的service方法,。

当第一次请求某个Servlet类时,StandardWrapper载入Servlet类,由于StandardWrapper实例会动态的载入该Servlet类,因此,它必须知道该Servlet类的完全限定名,可以调用StandardWrapper的setServletClass方法指定该servlet类的完全限定名,也可以调用其setName方法为该servlet类指定一个名字,

置于当StandardWrapperValue实例请求 加载 Servlet实例时,,StandardWrapper实例必须考虑到该Servlet类是否实现了SingleThreadModel接口,对于那些没有实现SingleThreadModel接口的servlet类,StandardWrapper只会载入该servlet类一次,并对随后的请求都返回该servlet类的同一个实例,StandardWrapper实例不需要多个servlet实例,因为它假设该servlet类的service方法在多线程环境中是线程安全的,如果必要的话,由servlet程序员来负责同步对共享资源的访问,

面对一个STM servlet类,事情就不同了,StandardWrapper实例必须保证每个时刻只能有一个线程在执行STM servlet实例的 service方法,如果StandardWrapper实例只维护一个STM servlet实例的话,下面是可能出现调用STM servlet实例的 service方法的代码

1  Servlet instance = wrapper.allocate();//从wrapper中获取了一个 servlet实例
2         if((instance  instanceof SingleThreadModel)){
3             synchronized (instance) {
4                 instance.service(request, response);
5             }
6         }else{
7             instance.service(request, response);
8         }

但是为了获得更好的性能,StandardWrapper实例会维护一个STM servlet实例池,Wrapper实例负责准备一个javax.servlet.servletConfig实例,后者在servlet实例内部可以获取到,

分配servlet实例

StandardWrapper实例的invoke方法会调用Wrapper实例的allocate方法获取请求的servlet的一个实例,因此StandardWrapper要实现allocate方法,给大家看下 其方法签名

 1 /**
 2      * 
 3      * 分配这个Servlet的初始化实例,该实例准备调用它的<code>service()</code>方法。如果servlet类没有实现
 4      * <code>SingleThreadModel</code>, 则可以立即返回(唯一的 后续再有请求该servlet的请求
 5      * 不会再创建新的实例)初始化实例。如果servlet类实现<code>SingleThreadModel</code>,
 6      * 则Wrapper实现必须确保这个实例在通过调用<code>deallocate()</code>释放之前不会被再次分配。
 7      *
 8      * @exception ServletException
 9      *                如果servlet init()方法抛出异常
10      * 
11      * @exception ServletException
12      *                如果发生加载错误
13      */
14     public Servlet allocate() throws ServletException {

注意:allocate方法返回请求servlet的一个实例,因为要支持 STM servlet,allocate方法需要变得复杂一点,事实上为了处理 STM servlet类 和 非STM servlet,allocate方法分为两个部分,第一部分

 1 if (!singleThreadModel) {  //返回一个 一个 非STM servlet的实例  } 

布尔变量 singleThreadModel用来标明该StandardWrapper实例标志的servlet类是否是STM servlet,该变量的初始值是false,LoadServlet方法会检查它正在载入的Servlet类是不是一个STM Servlet类,并根据结果修改变量singleThreadModel的值,

注意()

下面看下第一部分 和第二部分

对于非STM Servlet类,StandardWrapper类定义了一个 名为 instance,类型为 javax.servlet.Servlet的变量

/**
     * 若该 {@code StandardWrapper} 实例 所代表的Servlet 为非({@link SingleThreadModel}
     * Servlet)类, 则代表的该Servlet 只会被创建一次 ,{@code instance}存储只创建一次的Servlet实例
     */
    private Servlet instance = null;

allocate方法 会检查变量 instance 是否是 null,若是 则allocate方法调用LoadServlet方法载入相关的Servlet类,然后 将整型变量 countAllocated的值加1,并返回 instance的值代码如下

 1 // 如果不是SingleThreadedModel,每次返回相同的实例
 2         if (!singleThreadModel) {
 3             // 返回一个 一个 非STM servlet的实例
 4 
 5             // Load and initialize our instance if necessary
 6             if (instance == null) {
 7                 synchronized (this) {
 8                     if (instance == null) {
 9                         try {
10                             instance = loadServlet();
11                         } catch (ServletException e) {
12                             throw e;
13                         } catch (Throwable e) {
14                             throw new ServletException(sm.getString("standardWrapper.allocate"), e);
15                         }
16                     }
17                 }
18             }
19 
20             if (!singleThreadModel) {
21                 if (debug >= 2)
22                     log("  Returning non-STM instance");
23                 //将当前活动的Servlet数 加1 
24                 countAllocated++;
25                 return (instance);
26             }
27 
28         }

若StandardWrppper表示的Servlet类是一个STM Servlet类,则allocate方法会试图从对象池中返回一个Servlet实例,变量 instancePool是一个 java.util.Stack类型的栈,其中保存了所有的STM Servlet实例,

1    /**
2      * instancePool是一个 {@link java.util.Stack}类型的栈,其中保存了所有的STM Servlet实例
3      */
4     private Stack instancePool = null;

该变量在LoadServlet方法中初始化

只要STM Servlet实例数量不超过指定的最大数,allocate会返回一个STM Servlet实例。整型变量 maxInstances保存了在栈中存储的STM Servlet实例的最大值,默认值是20

  /** * 整型变量 maxInstances 保存了在栈中存储的STM Servlet实例的最大值,默认值是20  */  private int maxInstances = 20; 

为了跟踪当前 wrapper中 STM Servlet实例的数量,StandardWrapper类使用整型变量 nInstances来保存这个数值。

/**
     * 为了跟踪当前 wrapper中 STM Servlet实例的数量,StandardWrapper类使用整型变量 nInstances来保存这个数值。
     */
    private int nInstances = 0;

下面是allocate方法的第二部分

 

以上是关于StandardWrapper的主要内容,如果未能解决你的问题,请参考以下文章

StandardWrapper

tomcat(11)org.apache.catalina.core.StandardWrapper源码剖析

tomcat4标准包装器StandardWrapper

java.lang.classCaseException (load() Exception)

高分求java 的Servlet.destroy()异常,寻求解决办法

servlet常见错误1