Tomcat原理学习---StandardWrapper

在第五章中已经说过,一共有四种容器:engine(引擎),host(主机),context(上下文)和wrapper(包装器)。在前面的章节里也介绍了如何建立自己的context和wrapper。一个上下文一般包括一个或者多个包装器,每一个包装器表示一个servlet。本章将会看到Catalina 中Wrapper接口的标准实现。首先介绍了一个HTTP请求会唤醒的一系列方法,接下来介绍了javax.servlet.SingleThreadModel接口。最后介绍了StandardWrapper和StandardWrapperValve类


在学习StandardWrapper和StandarWrapperValve类之前,我们需要首先关注下javax.servlet.SingleThreadModel。理解该接口对于理解一个包装器是如何工作的是非常重要的。
SingleThreadModel :
一个servlet可以实现javax.servlet.SingleThreadModel接口,实现此接口的一个 servlet 通俗称为 SingleThreadModel(STM)的程序组件。根据 Servlet规范,实现此接口的目的是保证servlet一次只能有一个请求。


StandardWrapper :
一个StandardWrapper对象的主要职责是:加载它表示的servlet并分配它的一个实例。该StandardWrapper不会调用servlet的service方法。这个任务留给StandardWrapperValve 对象,在 StandardWrapper 实例的基本阀门管道。StandardWrapperValve 对象通过调用 StandardWrapper 的 allocate 方法获得Servlet实例。在获得Servlet实例之后的StandardWrapperValve调用servlet的service方法 在servlet第一次被请求的时候,StandardWrapper加载servlet类。它是动态的加载servlet,所以需要知道servlet类的完全限定名称。通过StandardWrapper类的setServletClass方法将servlet的类名传递给StandardWrapper。另外,使用setName方法也可以传递servlet名。 考虑到StandardWrapper负责在StandardWrapperValve请求的时候分配一个servlet实例,它必须考虑一个servlet是否实现了SingleThreadModel 接口。  如果一个servlet没有实现SingleThreadModel接口,StandardWrapper加载该servlet一次,对于以后的请求返回相同的实例即可。StandardWrapper假设servlet的service方法是现场安全的,所以并没有创建 servlet 的多个实例。如果需要的话,由程序员自己解决资源同步问题。


对于一个STM servlet,情况就有所不同了。StandardWrapper必须保证不能同时有两个线程提交STM servlet的service方法。但是,为了性能起见,StandardWrapper维护了一个STM servlet实例池。 


Allocating the Servlet:
在本节开始的时候介绍到StandardWrapperValve的invoke方法调用了包装器的allocate方法来获得一个请求servlet的实例。因此StandardWrapper类必须实现该接口
由于要支持STM servlet,这使得该方法更复杂了一点。实际上,该方法有两部分组成,一部分负责非STM servlet的工作,另一部分负责STM servlet。


Loading the Servlet :
StandardWrapper实现了Wrapper接口的load方法, load方法调用loadServlet方法来加载一个servlet类,并调用该servlet的init方法,传递一个javax.servlet.ServletConfig实例。
方法loadServlet首先检查StandardWrapper 是否表示一个STM servlet。如果不是并且该实例不是null(即以前已经加载过),直接返回该实例。如果该实例是null或者是一个STM servlet,继续该方法的其它部分。
首先获得System.out 和System.err输出,接下来以就可以使用javax.servlet.ServletContext 的log方法来记录信息。
然后,定义了一个javax.servlet.Servlet类型的变量,它表示loadServlet方法加载servlet后返回的实例。
方法loadServlet负责加载servlet类,类名应该被分配给servletClass变量,该方法将该值分配给一个String类型变量actualClass。0
但是,由于Catalina也是一个JSP容器,在请求的是JSP页面的时候,loadServlet必须也能工作,如果是JSP页面,则得到相应的Servlet类。
如果JSP页面的Servlet名字找不到,就是用 servletclass变量的值。但是,如果该变量的值没有使用StandardWrapper类中的setServletClass方法设置,会产生异常,剩余部分不会被执行。
现在,servlet的名字已经获得了,接下来是loadServlet方法获得加载器。如果找不到加载器,则产生异常并停止执行。
如果找到加载器,loadServlet方法调用它的 getClassLoader方法获得一个Catalina提供了特殊Servlet,从属于org.apache.catalina包。这些Servlet可以进入Servlet容器的内部。如果该Servlet是一个特殊Servlet,isContainerProvidedServlet方法返回true 值。classLoader会获得另一个ClassLoader的实例,这样就可以访问Catalina的内部了。 ClassLoader。 
有了类加载器和腰加载的Servlet名字,就可以使用loadServlet方法来加载类了。
但是,loadServlet方法在初始化Servlet之前,它使用isServletAllowed方法来检查该Servlet是否可以访问。  
如果通过了安全性检查,接下来检查该Servlet是否是一个ContainerServlet。ContainerServlet是实现了org.apache.catalina.ContainerServlet接口的Servlet,它可以访问Catalina的内部函数。如果该Servlet是ContainerServlet,loadServlet方法调用ContainerServlet的setWrapper方法,传递该StandardWrapper实例。 
接下来loadServlet方法触发BEFORE_INIT_EVENT事件,并调用发送者的init方法。
如果loadOnStartup变量的值大于零并且Servlet是一个JSP页面,调用该Servlet的service方法。  
接下来,loadServlet方法触发AFTER_INIT_EVENT事件
如果该StandardWrapper对象表示的是一个STM Servlet,将该实例添加到实例池中,因此,如果实例池如果为null,首先需要创建它。 
在finally块中,loadservlet方法会停止捕获System.out和System.err,并将加载过程中信息使用该容器的log方法记录到日志系统中。
最后,loadServlet方法返回Servlet实例。 


ServletConfig 对象 :
StandardWrapper的loadServlet方法在加载了loaded方法之后调用的发送者的init方法 。init方法传递一个javax.servlet.ServletConfig实例。
ServletConfig接口有以下四个方法getServletContext, getServletName, getInitParameter, 和getInitParameterNames。
注意:StandardWrapper并不将自己传递给Servlet 的init方法,它将自己包装到一个StandardWrapperFacade实例中来因此它的public方法


getServletContext :
一个StandardWrapper实例必须是一个StandardContext容器的子容器。也就是说, StandardWrapper的父容器时StandardContext。可以使用StandardContext 对象的getServletContext来获得ServletContext对象。
注意:现在你知道不能单独部署一个包装器来表示一个Servlet,包装器必须从属于一个上下文容器,这样才能使用ServletConfig对象使用getServletContext方法获得一个ServletContext实例。
getServletName :
它简单的调用StandardWrapper 的父类ContainerBase类的getName方法。可以使用setName方法来设置name的值。
getInitParameter :
该方法返回指定参数的值。
在StandardWrapper中,初始化参数被存放在一个名为parameters的HashMap中
可以调用StandardWrapper类的addInitParameter方法来填充parameters。传递参数的名字和值。
getInitParameterNames :
该方法返回所有初始化参数名字的枚举。


Parent and Children :
一个包装器表示一个独立Servlet的容器。这样,包装器就不能再有子容器,因此不可以调用它的addChild方法,如果调用了会得到一个java.langIllegalStateException。 
一个包装器的父容器只能是一个上下文容器。如果传递的参数不是一个上下文容器,它的setParent方法会抛出java.lang.IllegalArgumentException。


StandardWrapperFacade :
StandardWrapper调用它价值的Servlet 的init方法。该方法需要一个javax.servlet.ServletConfig的参数,而StandardWrapper类自己就实现了ServletConfig接口。所以,理论上StandardWrapper可以将它自己作为参数传递给init方法。但是StandardWrapper需要对Servlet隐藏他的大多数public方法。为了实现这一点,StandardWraper将它自己包装的一个StandardWrapperFacade实例中
当一个StandardWrapperFacade对象创建的时候,构造函数将该StandardWrapper负值给config变量 。
因此,当StandardWrapper对象调用Servlet 实例的init方法的时候,它传递的是一个StandardWrapperFacade对象。在Servlet内部调用ServletConfig的getServletName, getInitParameter, 和getInitParameterNames方法只需要调用它们在StandardWrapper的实现就行


StandardWrapperValve :
StandardWrapperValve是StandardWrapper实例上的基本阀门,该阀门做两件事情:
1、 提交Servlet的所有相关过滤器 
2、 调用发送者的service方法
要实现这些内容,下面是StandardWrapperValve在他的invoke方法要实现的:
1、 调用StandardWrapper的allocate的方法来获得一个servlet实例
2、 调用它的private createFilterChain方法获得过滤链 
3、 调用过滤器链的doFilter方法。包括调用servlet的service方法
4、 释放过滤器链 
5、 调用包装器的deallocate方法 
6、 如果Servlet无法使用了,调用包装器的unload方法


FilterDef :
org.apache.catalina.deploy.FilterDef表示一个过滤器定义,就像是在部署文件中定义一个过滤器元素那样。
FilterDef类中的每一个属性都代表一个可以在过滤器中出现的子元素。该类包括一个Map类型的变量表示一个包含所有初始参数的Map。方法addInitParameer添加一个name/value对到该Map。


ApplicationFilterConfig:
org.apache.catalina.core.ApplicationFilterConfig实现了javax.servlet.FilterConfig接口。
ApplicationFilterConfig负责管理web应用程序启动的时候创建的过滤器实例。 
传递一个org.apache.catalina.Context对象和ApplicationFilterConfig对象给ApplicationFilterConfig的构造来创建一个ApplicationFilterConfig实例:
public ApplicationFilterConfig(Context context, FilterDef filterDef)  throws ClassCastException, ClassNotFoundException,  IllegalAccessException, InstantiationException, ServletException 
Context对象表示一个一个web应用而FilterDef表示一个过滤器定义。ApplicationFilterConfig的getFilter方法可以返回一个javax.servlet.Filter方法,该方法加载过滤器类并初始化它。


ApplicationFilterChain:
org.apache.catalina.core.ApplicationFilterChain类是实现了javax.servlet.FilterChain接口。StandardWrapperValve类中的invoke方法创建一个该类的实例并且调用它的doFilter 方法。ApplicationFilterChain类的doFilter的调用该链中第一个过滤器的doFilter方法


Filter接口中doFilter方法的签名如下: 
public void doFilter(ServletRaquest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException 


ApplicationFilterChain的doFilter方法,并将它自己作为第三个参数传递给它。 在他的doFilter方法中,一个过滤器可以调用另一个过滤器链的doFilter来唤醒另一个过来出去。


在doFilter方法最后一行,它调用过滤链的doFilter方法。如果该过滤器是过滤链的最后一个过滤器,它叫调用请求的Servlet的service方法。如果过滤器没有调用chain.doFilter,下一个过滤器就不会被调用。

你可能感兴趣的:(Tomcat原理学习---StandardWrapper)