StandardWrapper

学向勤中得,萤窗万卷书。这篇文章主要讲述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方法的代码
1Servlet instance = wrapper.allocate(); //从wrapper中获取了一个 servlet实例 2if((instanceinstanceof SingleThreadModel)){ 3synchronized (instance) { 4instance.service(request, response); 5} 6}else{ 7instance.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*/ 14public 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,每次返回相同的实例 2if (!singleThreadModel) { 3// 返回一个 一个 非STM servlet的实例 4 5// Load and initialize our instance if necessary 6if (instance == null) { 7synchronized (this) { 8if (instance == null) { 9try { 10instance = loadServlet(); 11} catch (ServletException e) { 12throw e; 13} catch (Throwable e) { 14throw new ServletException(sm.getString("standardWrapper.allocate"), e); 15} 16} 17} 18} 19 20if (!singleThreadModel) { 21if (debug > = 2) 22log("Returning non-STM instance"); 23//将当前活动的Servlet数 加1 24countAllocated++; 25return (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*/ 4private 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】 

    推荐阅读