StandardWrapper
Posted 陈东
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了StandardWrapper相关的知识,希望对你有一定的参考价值。
Tomcat中有四种类型的Servlet容器,分别是 Engine、Host、Context、Wrapper,每个Wrapper实例表示一个具体的Servlet定义,StandardWrapper就是Catalina中的Wrapper接口的标准实现.
方法调用序列:指的是每当服务器接收到Http请求时,服务器中调用的一系列方法,对于每个引入的HTTP请求,连接器都会调用与其关联的Servlet容器的 invoke方法,然后,Servlet容器会调用其所有子容器的invoke方法,
具体的过程看下面:
- 连接器创建request和response对象;
- 连接器调用StandardContext实例的invoke方法,
- 接着StandardContext实例的invoke方法会调用其管道对象的额invoke方法,StandardContext对象的基础阀是StandardContextValue类的实例,因此StandardContext的管道对象会调用其基础阀的invoke方法,
- StandardContextValue实例的invoke方法会获取响应的Wrapper实例来处理HTTP请求,调用Wrapper实例的invoke方法
- StandardWrapper类是Wrapper接口的标准实现,StandardWrapper对象会调用其管道对象的invoke方法。
- StandardWrapper对象的基础阀是StandardWrapperValue类的实例,因此会调用StandardWrapperValue的invoke方法,其invoke方法会调用Wrapper实例的allocate方法获取servlet实例;
- allocate方法会调用load方法载入相应的servlet类,若已经载入,咋无需重复载入,
- load方法会调用servlet实例的init方法
- 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的主要内容,如果未能解决你的问题,请参考以下文章
tomcat(11)org.apache.catalina.core.StandardWrapper源码剖析
java.lang.classCaseException (load() Exception)