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方法的第二部分

 1 synchronized (instancePool) {
 2             // 为毛这样写呢 因为 就算该Wrapper代表的是 STM servlet,第一次执行alllocate方法的时候
 3             // 布尔变量 singleThreadModel 的值 是为false的,它会执行上面第一部分 非 STM servlet加载流程,
 4             // 在其调用loadServlet方法是时 其方法内部会根据 是否是继承了 SingleThreadModel接口来
 5             // 修改布尔变量singleThreadModel的值
 6             // 所以初始到这里时 为 countAllocated = 0 nInstances = 0;
 7             // 然后在while循环中直到 STM Servlet实例的数量与要大于 countAllocated 循环中 只会 将
 8             // nInstances的数量增加 而 countAllocated是在循环结束后增加
 9             while (countAllocated >= nInstances) {
10                 // 如果可能的话,分配一个新的实例,或者等待
11                 if (nInstances < maxInstances) {
12                     // 如果当前活动的STM Servlet实例数量 没有达到允许的最大上限 则 创建新的 Servlet实例
13                     // 并将其加入到 STM Servlet 实例池中
14                     try {
15                         instancePool.push(loadServlet());
16                         // 将活动的STM Servlet实例数量 加 1
17                         nInstances++;
18                     } catch (ServletException e) {
19                         throw e;
20                     } catch (Throwable e) {
21                         throw new ServletException(sm.getString("standardWrapper.allocate"), e);
22                     }
23                 } else {
24                     try {
25                         // 若 当前活动的 STM servlet实例 数量已经达到了最大值 则将线程挂起 等待 有被用完的 STM
26                         // serlvlet实例 被放回到栈中
27                         instancePool.wait();
28                     } catch (InterruptedException e) {
29                         ;
30                     }
31                 }
32             }
33             if (debug >= 2)
34                 log("  Returning allocated STM instance");
35             // 将当前StandardWrapper中活动的 servlet实例数加一
36             countAllocated++;
37             // 并将 栈顶的STM servlet实例 取出
38             return (Servlet) instancePool.pop();
39
40         }

载入Servlet类

StandardWrapper实例实现了 Wrapper接口的 load方法,load方法调用 loadServlet方法载入某个Servlet类,并调用其init方法,此时要传入一个 javax.servlet.ServletConfig实例作为参数,下面展示一下 loadServlet方法是怎么工作的

loadServlet方法首先会检查当前的StandardWrapper类是否表示的是一个STM Servlet类,若不是 且 变量 instance不为null (表示以前已经载入过这个Servlet类)。它就直接返回该实例

 1 /**
 2      *
 3      *
 4      * 如果还没有至少一个初始化的实例,则加载并初始化此servlet的实例。例如,这可以用于加载部署描述符中标记的、
 5      * 要在服务器启动时加载的servlet。
 6      */
 7     public synchronized Servlet loadServlet() throws ServletException {
 8
 9         // 检查该StandardWrapper类表示的是否是一个STM Servlet类,若不是,且 instance变量
10         // 不为null(代表之前载入过这个servlet类),直接返回该实例
11         if (!singleThreadModel && (instance != null))
12             return instance;

若instance 为 null 或者 该servlet实例是一个STM servlet,则执行后续的方法,、

首先,它会获取System.out和 System.error的输出,便于它使用javax.servlet.ServletContext的log方法记录日志信息

1 PrintStream out = System.out; 2 SystemLogHandler.startCapture();

然后它定义类型为javax.servler.Servlet名为servlet的变量,变量servlet表示已经载入的servlet实例,该实例将会有loadServlet方法返回

// 变量servlet表示已经载入的servlet实例,该实例将会有loadServlet方法返回Servlet servlet = null;

LoadServlet方法负责载入该Servlet类,原先类名会保存在类变量 ServletClass中,现在loadServlet方法要将变量名写入到字符串actualClass中;

//LoadServlet方法负责载入该Servlet类,原先类名会保存在类变量 ServletClass中,现在loadServlet方法要将变量名写入到字符串actualClass中;String actualClass = servletClass;

但是由于Catalina也是一个JSP容器,因此loadServlet方法必须检查请求的Servlet是不是一个jsp页面,若是,LoadServlet方法需要获取代表该JSP页面的实际的servlet类;

1             //因为Catalina也是一个JSP容器,因此loadservlet方法必须检查请求的servlet是不是一个JSP页面,若是 loadServlet方法需要获取代表该JSP页面的实际servlet类
2             if ((actualClass == null) && (jspFile != null)) {
3                 Wrapper jspWrapper = (Wrapper) ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
4                 if (jspWrapper != null)
5                     actualClass = jspWrapper.getServletClass();
6             }

如果找不到该JSP页面的实际的servlet类,则会使用变量 servletClass的值,但是若是没有调用StandardWrapper类的setServletClass方法设置ServletClass的值,则会抛出异常,并停止执行后续运行的方法

1 // 如果找不到该JSP页面的实际的servlet类,则会使用变量
2             // servletClass的值,但是若是没有调用StandardWrapper类的setServletClass方法设置ServletClass的值,则会抛出异常,并停止执行后续运行的方法
3             if (actualClass == null) {
4                 unavailable(null);
5                 throw new ServletException(sm.getString("standardWrapper.notClass", getName()));
6             }

这时 要载入的Servlet类名已经解析完成,loadServlet方法会获取载入器,若找不到载入器则它会抛出异常,方法终止

1 // 这时 要载入的Servlet类名已经解析完成,loadServlet方法会获取载入器,若找不到载入器则它会抛出异常,方法终止
2             Loader loader = getLoader();
3             if (loader == null) {
4                 unavailable(null);
5                 throw new ServletException(sm.getString("standardWrapper.missingLoader", getName()));
6             }

若可以找到载入器,则loadServlet方法会调用载入器的getClassLoader方法获取一个ClassLoader;

// 若可以找到载入器,则loadServlet方法会调用载入器的getClassLoader方法获取一个ClassLoader;ClassLoader classLoader = loader.getClassLoader();

在org.apache.catalina包下,Catalina提供了一写用于访问Servlet容器内部数据的专用Servlet类,如果某个Servlet类时这种专用的Servlet,即 isContainerProviededServlet方法返回true,则变量classLoader的赋值为另外一种ClassLoader实例,如此一来,

这个通过特殊classLoader加载出来的Servlet实例就可以访问Catalina内部数据了。

1             // 在org.apache.catalina包下,Catalina提供了一写用于访问Servlet容器内部数据的专用Servlet类,如果某个Servlet类时这种专用的Servlet,即
2             // isContainerProviededServlet方法返回true,则变量classLoader的赋值为另外一种ClassLoader实例,如此一来,
3             // 这个通过特殊classLoader加载出来的Servlet实例就可以访问Catalina内部数据了。
4             if (isContainerProvidedServlet(actualClass)) {
5                 classLoader = this.getClass().getClassLoader();
6                 log(sm.getString("standardWrapper.containerServlet", getName()));
7             }

有了载入器和准备载入的Servlet类名之后,loadServlet方法就可以载入Servlet类了

 1 //有了载入器和准备载入的Servlet类名之后,loadServlet方法就可以载入Servlet类了
 2             Class classClass = null;
 3             try {
 4                 if (classLoader != null) {
 5                     System.out.println("Using classLoader.loadClass");
 6                     classClass = classLoader.loadClass(actualClass);
 7                 } else {
 8                     System.out.println("Using forName");
 9                     classClass = Class.forName(actualClass);
10                 }
11             } catch (ClassNotFoundException e) {
12                 unavailable(null);
13                 throw new ServletException(sm.getString("standardWrapper.missingClass", actualClass), e);
14             }
15             if (classClass == null) {
16                 unavailable(null);
17                 throw new ServletException(sm.getString("standardWrapper.missingClass", actualClass));
18             }

然后实例化该Servlet类

 1 //然后 实例化该Servlet类
 2             try {
 3                 servlet = (Servlet) classClass.newInstance();
 4             } catch (ClassCastException e) {
 5                 unavailable(null);
 6                 // Restore the context ClassLoader
 7                 throw new ServletException(sm.getString("standardWrapper.notServlet", actualClass), e);
 8             } catch (Throwable e) {
 9                 unavailable(null);
10                 // Restore the context ClassLoader
11                 throw new ServletException(sm.getString("standardWrapper.instantiate", actualClass), e);
12             }

在实例化之后 立即检查该Servlet是否允许载入,若不允许立即终止

1 // 实例化Servlet类之后 会立即检查该Servlet类是否允许载入 若不允许载入 终止
2             if (!isServletAllowed(servlet)) {
3                 throw new SecurityException(sm.getString("standardWrapper.privilegedServlet", actualClass));
4             }

若通过了安全检查,它会继续检查该Servlet类是否是一个ContainerServlet类型的Servlet,实现了org.apache.catalina.ContainerServlet接口的Servlet可以访问Catalina的内部功能,若该servlet类是一个ContainerServlet,loadServlet方法会调用

ContainerServlet接口的setWrapper方法,传入这个StandardWrapper实例。

1 // 若通过了安全检查,它会继续检查该Servlet类是否是一个ContainerServlet类型的Servlet,实现了org.apache.catalina.ContainerServlet接口的Servlet可以访问Catalina的内部功能,若该servlet类是一个ContainerServlet,loadServlet方法会调用
2             // ContainerServlet接口的setWrapper方法,传入这个StandardWrapper实例。
3             if ((servlet instanceof ContainerServlet) && isContainerProvidedServlet(actualClass)) {
4                 System.out.println("calling setWrapper");
5                 ((ContainerServlet) servlet).setWrapper(this);
6                 System.out.println("after calling setWrapper");
7             }

接下来,loadServlet方法触发 BEFORE_INIT_EVENT事件,调用Servlet实例的init方法

1 // 接下来,loadServlet方法触发 BEFORE_INIT_EVENT事件,调用Servlet实例的init方法
2             try {
3                 instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT, servlet);
4                 //facade 为 javax.servlet.ServletConfig对象的一个外观变量
5                 servlet.init(facade);

继续展示下一部分代码前 先引入一个 变量的解释 loadOnStartup

我们在web.xml中配置servlet的时候会有个属性<load-on-startup></load-on-startup>,这里主要记一下它的作用,源码在后续记得好好看一下。

The load-on-startup element indicates that this servlet should be loaded (instantiated and have its init() called) on the startup of the web application. The optional contents of these element must be an integer indicating the order in which the servlet should be loaded. If the value is a negative integer, or the element is not present, the Container is free to load the servlet whenever it chooses.   If the value is a positive integer or 0, the container must load and initialize the servlet as the application is deployed. The container must guarantee that servlets marked with lower integers are loaded before servlets marked with higher integers. The container may choose the order of loading of servlets with the same load-on-start-up value.
意思大概:

  1. load-on-startup 元素标记容器是否应该在web应用程序启动的时候就加载这个servlet,(实例化并调用其init()方法)。
  2. 它的值必须是一个整数,表示servlet被加载的先后顺序。
  3. 如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载。
  4. 如果值为正整数或者0时,表示容器在应用启动时就加载并初始化这个servlet,值越小,servlet的优先级越高,就越先被加载。值相同时,容器就会自己选择顺序来加载。

初始值为-1;

 1 /**
 2      *
 3      * StandardWrapper代表的servlet的启动时加载顺序值(负值表示第一次调用时的加载)。
 4      * <p>
 5      * <b>load-on-startup</b>
 6      * 元素标记容器是否应该在web应用程序启动的时候就加载这个servlet,(实例化并调用其init()方法)。
 7      * 它的值必须是一个整数,表示servlet被加载的先后顺序。 如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载。
 8      * 如果值为正整数或者0时,表示容器在应用启动时就加载并初始化这个servlet,值越小,servlet的优先级越高,就越先被加载。值相同时,
 9      * 容器就会自己选择顺序来加载
10      * </p>
11      */
12     private int loadOnStartup = -1;

若变量 loadOnstartup的值大于0,而被请求的Servlet类实际上是一个jsp页面,则也调用Servlet的service方法

1 // 若变量 loadOnstartup的值大于0,而被请求的Servlet类实际上是一个jsp页面,则也调用Servlet的service方法
2                 if ((loadOnStartup > 0) && (jspFile != null)) {
3                     // Invoking jspInit
4                     HttpRequestBase req = new HttpRequestBase();
5                     HttpResponseBase res = new HttpResponseBase();
6                     req.setServletPath(jspFile);
7                     req.setQueryString("jsp_precompile=true");
8                     servlet.service(req, res);
9                 }

接下来loadServlet方法会触发AFTER_INIT_EVENT事件

    //接下来loadServlet方法会触发AFTER_INIT_EVENT事件instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet);

若StandardWrapper对象表示的Servlet类是一个STM servlet, 将SingleThreadModel类变量赋值为 true否则false 然后将该Servlet实例添加到STM servlet实例池中,因此会先判断变量 instancePool的值是否为null,若是则要给它赋值一个Stack对象:

1             // 若StandardWrapper对象表示的Servlet类是一个STM servlet,
2             // 将SingleThreadModel类变量赋值为 true否则false 然后将该Servlet实例添加到STM
3             // servlet实例池中,因此会先判断变量 instancePool的值是否为null,若是则要给它赋值一个Stack对象:
4             singleThreadModel = servlet instanceof SingleThreadModel;
5             if (singleThreadModel) {
6                 if (instancePool == null)
7                     instancePool = new Stack();
8             }
9             fireContainerEvent("load", this);

在finally代码块中,loadServlet方法停止捕获System.out 和 System.error对象,记录在早日ServletContext的log方法过程中产生的日志消息

 1 finally {
 2             //停止捕获System.out 和 System.err对象,记录在载入ServletContext的log方法的过程中产生的日志消息
 3             String log = SystemLogHandler.stopCapture();
 4             if (log != null && log.length() > 0) {
 5                 if (getServletContext() != null) {
 6                     getServletContext().log(log);
 7                 } else {
 8                     out.println(log);
 9                 }
10             }
11         }

最后返回已经载入的Servlet类

return servlet;

ServletConfig对象

StandardWrapper类的LoadServlet在载入Servlet类后,会调用该Servlet实例的init方法,init方法需要传入一个javax.servlet.ServletConfig实例作为参数,那这个ServletCongfig 是咋被StandardWrapper类获取的捏,请看

 1 /**
 2  *
 3  * <p>
 4  * <b>Title:StandardWrapper.java</b>
 5  * </p>
 6  * <p>
 7  * Copyright:ChenDong 2018
 8  * </p>
 9  * <p>
10  * Company:仅学习时使用
11  * </p>
12  * <p>
13  * 类功能描述: 表示单个servlet定义的Wrapper接口的标准实现。不允许子容器,父容器必须是{@link Context}。
14  * </p>
15  *
16  * @author 陈东
17  * @date 2018年12月1日 下午2:57:33
18  * @version 1.0
19  */
20 public final class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper {

因为它本身就已经继承了 javax.servlet.ServletConfig接口,那么下面给大家展示一下ServletConfig接口的4个方法;

  1. getServletContext()
  2. getServletName()
  3. getInitParameter
  4. getInitParameterNames()

注意:StandardWrapper 类并不会将自身传递给Servlet实例的init方法,它会在一个StandardWrapperFacade实例中包装自身,将绝大多数的公共方法对 Servlet程序员隐藏起来。

先说下第一个方法:getServletContext方法 先看下方法签名xt getServletContext();

public ServletContext getServletContext() 

StandardWrapper实例肯定是StandardContext实例的子容器,也就是说StandardWrapper实例的父容器就是StandardContext实例,对于StandardContext对象来说,可以直接调用自身的getServletContext方法来获得一个ServletContext实例,下面是StandardWrapper中该方法的具体实现

 1 /**
 2      * 返回与此{@link Servlet}关联的{@link ServletContext }上下文.
 3      */
 4     public ServletContext getServletContext() {
 5
 6         if (parent == null)
 7             return (null);
 8         else if (!(parent instanceof Context))
 9             return (null);
10         else
11             return (((Context) parent).getServletContext());
12
13     }

注意:从上面的代码也是可以看出 ,无法单独使用一个Wrapper实例来表示一个servlet类的定义,Wrapper实例必须存在于某个Context容器中,这样当调用其父容器的getServletContext方法时才能返回ServletContext类的一个实例

第二个方法 getServletName()

该方法返回Servlet类的名字,方法签名如下

java.lang.String getServletName();

那么在StandardWrapper中的实现如下

1 /**
2      * 返回这个Servlet的名字.
3      */
4     public java.lang.String getServletName() {
5
6         return (getName());
7
8     }

该方法仅仅是简单调用了ContainerBase类(StandardWrapper的父类)的getName方法,ContainerBase类的 getName方法实现如下

public String getName() {return (name);}

可以通过setName方法设置变量name的值,通过传递Servlet的名称可以调用StandardWrapper实例的setName方法。

第三个方法 getInitParameter方法

该方法返回指定初始参数的值,该方法签名如下:

java.lang.String getInitParameter(java.lang.String name);

在StandardWrapper中,初始化参数存储在一个HashMap类型的名为parameters的变量中

1     /**
2      *
3      * 存储初始化参数的HashMap类型的变量,key值为参数名称
4      */
5     private HashMap parameters = new HashMap();

那么这个parameters是怎么填充值得呢?调用StandardWrapper类的addInitParameter方法,并传入参数的名字和对应的值来填充parameters的值

 1 /**
 2      *
 3      * 向存储初始化参数的parameters变量中 添加一对新的初始化参数
 4      *
 5      * @param name
 6      *            要添加的初始化参数名称 Name of this initialization parameter to add
 7      * @param value
 8      *            要添加的初始参数的值
 9      */
10     public void addInitParameter(String name, String value) {
11
12         synchronized (parameters) {
13             parameters.put(name, value);
14         }
15         fireContainerEvent("addInitParameter", name);
16
17     }

那么StandardWrapper 中getInitParameter()方法的实现如下

 1 /**
 2      *
 3      *
 4      * 返回指定名称的初始化参数值(如果有的话);否则返回<code>null</code>.
 5      *
 6      * @param name
 7      *            要检索的初始化参数的名称
 8      *
 9      *
10      */
11     public String getInitParameter(String name) {
12
13         return (findInitParameter(name));
14
15     }

其中findInitParameter方法接收一个指定的初始化参数名的字符串变量,调用HashMap变量 parameters的get方法获取初始化参数的值,下面是具体实现

 1 /**
 2      * 返回指定名称的初始化参数值(如果有的话);否则返回<code>null</code>.
 3      *
 4      * @param name
 5      *            要检索的初始化参数的名称
 6      */
 7     public String findInitParameter(String name) {
 8
 9         synchronized (parameters) {
10             return ((String) parameters.get(name));
11         }
12
13     }

第四个方法:getInitParameterNames()方法

该方法返回所有初始化参数的名字的集合,实际上是一个枚举类型java.util.Enumeraton的实例,下面是该方法的签名

java.util.Enumeration getInitParameterNames();

那么在看下StandardWrapper中的具体实现

 1 /**
 2      * 返回为该servlet定义的初始化参数名称集。如果没有定义,则返回空枚举。
 3      */
 4     public Enumeration getInitParameterNames() {
 5
 6         synchronized (parameters) {
 7             return (new Enumerator(parameters.keySet()));
 8         }
 9
10     }

其中 Enumerator实现了java.util.Enumeration接口,

  1 package org.apache.catalina.util;
  2
  3 import java.util.Collection;
  4 import java.util.Enumeration;
  5 import java.util.Iterator;
  6 import java.util.Map;
  7 import java.util.NoSuchElementException;
  8
  9 /**
 10  *
 11  * <p>
 12  * <b>Title:Enumerator.java</b>
 13  * </p>
 14  * <p>
 15  * Copyright:ChenDong 2018
 16  * </p>
 17  * <p>
 18  * Company:仅学习时使用
 19  * </p>
 20  * <p>
 21  * 类功能描述:
 22  * 围绕Java2集合类对象Iterator包装枚举的适配器类,以便现有的返回枚举的API可以轻松地在新集合上运行。提供构造函数来容易地创建这样的包装器。
 23  * </p>
 24  *
 25  * @author 26  * @date 2018年12月1日 下午4:01:40
 27  * @version 1.0
 28  */
 29 public final class Enumerator implements Enumeration {
 30
 31     // ----------------------------------------------------------- Constructors
 32
 33     /**
 34      *
 35      * 在指定集合的值上返回枚举。
 36      *
 37      * @param collection
 38      *            要返回枚举值的集合
 39      */
 40     public Enumerator(Collection collection) {
 41
 42         this(collection.iterator());
 43
 44     }
 45
 46     /**
 47      * 在指定迭代器返回的值上返回枚举。
 48      *
 49      *
 50      *
 51      * @param iterator
 52      *            要被包装的迭代器
 53      */
 54     public Enumerator(Iterator iterator) {
 55
 56         super();
 57         this.iterator = iterator;
 58
 59     }
 60
 61     /**
 62      * 在指定映射的所有value返回枚举
 63      *
 64      * @param map
 65      *            要返回枚举值得映射
 66      */
 67     public Enumerator(Map map) {
 68
 69         this(map.values().iterator());
 70
 71     }
 72
 73     // ----------------------------------------------------- Instance Variables
 74
 75     /**
 76      *
 77      * 这个类所表示的<code>Enumeration</code>实际操作的<code>Iterator</code>。
 78      */
 79     private Iterator iterator = null;
 80
 81     // --------------------------------------------------------- Public Methods
 82
 83     /**
 84      *
 85      * 检查该枚举是否包含更多元素
 86      *
 87      *
 88      * @return 当且仅当该枚举对象包含要提供的至少一个元素,则为<code>true</code>,否则为<code>true</code>
 89      */
 90     public boolean hasMoreElements() {
 91
 92         return (iterator.hasNext());
 93
 94     }
 95
 96     /**
 97      * 如果该枚举要提供至少一个元素,则返回该枚举的下一个元素.
 98      *
 99      * @return 这个枚举的下一个元素
100      *
101      * @exception NoSuchElementException
102      *                如果没有更多的元素存在
103      */
104     public Object nextElement() throws NoSuchElementException {
105
106         return (iterator.next());
107
108     }
109
110 }

Servlet容器的父子关系

Wrapper实例代表一个Servlet实例,因此Wrapper实例不能再有子容器了,不应该在调用其addChild方法,否则抛出java.lang.IllegalStateException异常,下面是StandardWrapper类中的addChild方法的实现;

 1 /**
 2      * Wrapper实例代表一个Servlet实例,因此Wrapper实例不能再有子容器了,不应该在调用其addChild方法, 否则抛出{@code
 3      * java.lang.IllegalStateException}异常,下面是{@link StandardWrapper}类中的
 4      * {@code addChild}方法的实现;
 5      *
 6      * @param child
 7      *            要被添加的子容器
 8      */
 9     public void addChild(Container child) {
10
11         throw new IllegalStateException(sm.getString("standardWrapper.notChild"));
12
13     }

说完了子容器 也就是Wrapper 不能有儿子,但是人家是可以有老子滴,当然了Wrapper的父容器只能是Context类的实现,若是在调用Wrapper实例的setParaent方法时传入了一个非Context类型的容器,则会抛出

java.lang.IllegalAumentException异常

 1 /**
 2      *
 3      *
 4      * <dd>为当前的Container也就是StandardWrapper 设置父容器,其父容器只能是Context类的实现</dd>
 5      *
 6      *
 7      * @param container
 8      *            要设置的父容器
 9      * @exception IllegalArgumentException
10      *                传入了一个非Context类型的容器
11      */
12     public void setParent(Container container) {
13
14         if ((container != null) && !(container instanceof Context))
15             throw new IllegalArgumentException(sm.getString("standardWrapper.notContext"));
16         super.setParent(container);
17
18     }

StandardWrapperFacade

下面来谈一下 StandardWrapperFacade类 也就是 StandardWrapper的外观类,

咱们上面说到过,StandardWrapper实例会调用它所代表的Servlet实例的init方法,init方法需要一个javax.servlet.ServletConfig实例,而StandardWrapper类本身就实现了 javax.servlet.ServletConfig接口,所以理论上StandardWrapper可以将自己传入Servlet的init方法中,

但是StandardWrapper类要将其大部分公共方法对Servlet程序员隐藏起来,为了实现这个目的StandardWrapper类将自身实例包装成StandardWrapperFacade类的一个实例,这个其实就是外观类的设计模式,有兴趣可以学习一下,

StandardWrapper 与 StandardWrapperFacade 都共同继承了javax.servlet.ServletConfig接口,(就是想要使用的是一个什么样的外观类 就让它继承什么接口 这样外观类就有了需要使用的全部方法,然后外观类中会有一个声明成接口类型 原类私有实例 变量,且还有接口方法,方法的全部实现都调用原类的方法),那么下面就详细展示一体

StandardWrapper类使用下面的代码来创建StandardWrapperFacade类的一个实例,需要将自身的实例作为参数传入到StandardWrapperFacade类的构造函数中:

1    /**
2      * 该{@link StandardWrapper}类的 {@link ServletConfig}类型的外观类
3      */
4     private StandardWrapperFacade facade = new StandardWrapperFacade(this);

StandardWrapperFacde类提供了一个私有 ServletConfig类型的类级别的变量 config 来保存原类的实例

   /*** 保存 该外观类封装的 原类*/private ServletConfig config = null;

当在StandardWrapper对象内部创建StandardWrapperFacde类的一个实例时,StandardWrapperFacade类的构造函数需要传入StandardWrapper对象,并未变量 config赋值

1     /**
2      * 创建一个包装了 指定 {@link StandardWrapper} 的外观类
3      */
4     public StandardWrapperFacade(StandardWrapper config) {
5
6         super();
7         this.config = (ServletConfig) config;
8
9     }

因此在StandardWrapper 中 调用 Servlet 的 init方法时,会传入一个同样实现了javax.servlet.ServletConfig接口的 StandardWrapperFacade的一个实例,这样在Servlet实例内调用ServletConfig类的方法的时候,StandardWrapperFacade实例会直接调用 包装的StandardWrapper类的相应方法;

 1 package org.apache.catalina.core;
 2
 3 import java.util.Enumeration;
 4 import javax.servlet.ServletConfig;
 5 import javax.servlet.ServletContext;
 6
 7 /**
 8  *
 9  * <p>
10  * <b>Title:StandardWrapperFacade.java</b>
11  * </p>
12  * <p>
13  * Copyright:ChenDong 2018
14  * </p>
15  * <p>
16  * Company:仅学习时使用
17  * </p>
18  * <p>
19  * 类功能描述: {@link StandardWrapper }类的 {@link ServletConfig }类型的外观类
20  * </p>
21  *
22  * @author 陈东
23  * @date 2018年12月1日 下午5:12:01
24  * @version 1.0
25  */
26 public final class StandardWrapperFacade implements ServletConfig {
27
28     // ----------------------------------------------------------- Constructors
29
30     /**
31      * 创建一个包装了 指定 {@link StandardWrapper} 的外观类
32      */
33     public StandardWrapperFacade(StandardWrapper config) {
34
35         super();
36         this.config = (ServletConfig) config;
37
38     }
39
40     // ----------------------------------------------------- Instance Variables
41
42     /**
43      * 保存 该外观类封装的 原类
44      */
45     private ServletConfig config = null;
46
47     // -------------------------------------------------- ServletConfig Methods
48
49     /**
50      * 返回Servlet的名称
51      */
52     public String getServletName() {
53         return config.getServletName();
54     }
55
56     /**
57      * 获取ServletContext 实例,这里 获取的是 ServletContext的外观类
58      */
59     public ServletContext getServletContext() {
60         ServletContext theContext = config.getServletContext();
61         if ((theContext != null) && (theContext instanceof ApplicationContext))
62             theContext = ((ApplicationContext) theContext).getFacade();
63         return (theContext);
64     }
65
66     /**
67      * 返回指定的初始化参数 值
68      */
69     public String getInitParameter(String name) {
70         return config.getInitParameter(name);
71     }
72
73     /**
74      * 返回 所有初始化参数的名字枚举 若无初始化参数 则返回一个空的枚举
75      */
76     public Enumeration getInitParameterNames() {
77         return config.getInitParameterNames();
78     }
79
80 }

对getServletContext方法会做一些处理 并不返回 ServletContext本身而是返回其外观类,

为了更好的理解 下面的 SrandardWrapperValue功能 ,首先引入一个新的组件定义:

FilterDef类

org.apache.catalina.deploy.FilterDef类表示一个过滤器定义,过滤器通常都是 在Tomcat 部署描述文件中定义filter元素

  1 package org.apache.catalina.deploy;
  2
  3 import java.util.HashMap;
  4 import java.util.Map;
  5
  6 /**
  7  *
  8  * <p>
  9  * <b>Title:FilterDef.java</b>
 10  * </p>
 11  * <p>
 12  * Copyright:ChenDong 2018
 13  * </p>
 14  * <p>
 15  * Company:仅学习时使用
 16  * </p>
 17  * <p>
 18  * 类功能描述:Web应用程序的过滤器定义的表示,如部署描述符中的{@code <filter>}元素所示。
 19  * </p>
 20  *
 21  * <p>
 22  * 该类中的全部属性 都对应 web部署描述中的{@code <filter>}元素 ,
 23  * </p>
 24  * <p>
 25  * {@code <filter>}元素中包含六个子元素
 26  * </p>
 27  * <p>
 28  * {@code icon }可选元素。该元素声明了一个供IDE使用的图片
 29  * </p>
 30  * <p>
 31  * {@code filter-name}必需元素,该元素给过滤器设置一个名称。
 32  * </p>
 33  * <p>
 34  * {@code display-name}可选元素。设置一个给IDE使用的简短名称
 35  * </p>
 36  * <p>
 37  * {@code description} 可选元素。该元素给IDE提供过滤器的文件文档内容
 38  * </p>
 39  * <p>
 40  * {@code filter-class} 必需元素。指定过滤器实现类的完全限定名。
 41  * </p>
 42  * <p>
 43  * {@code init-param} 可选元素,定义过滤器的初始化参数,一个过滤器可以包含多个初始化参数
 44  * </p>
 45  *
 46  *
 47  * @author 陈东
 48  * @date 2018年12月1日 下午9:23:03
 49  * @version 1.0
 50  */
 51 public final class FilterDef {
 52
 53     // ------------------------------------------------------------- Properties
 54
 55     /**
 56      * (可选元素)该过滤器的说明
 57      */
 58     private String description = null;
 59
 60     public String getDescription() {
 61         return (this.description);
 62     }
 63
 64     public void setDescription(String description) {
 65         this.description = description;
 66     }
 67
 68     /**
 69      * 此过滤器的显示名称.
 70      */
 71     private String displayName = null;
 72
 73     public String getDisplayName() {
 74         return (this.displayName);
 75     }
 76
 77     public void setDisplayName(String displayName) {
 78         this.displayName = displayName;
 79     }
 80
 81     /**
 82      * (必须元素)实现此过滤器的Java类的完全限定名
 83      */
 84     private String filterClass = null;
 85
 86     public String getFilterClass() {
 87         return (this.filterClass);
 88     }
 89
 90     public void setFilterClass(String filterClass) {
 91         this.filterClass = filterClass;
 92     }
 93
 94     /**
 95      * (必需元素)这个过滤器的名称,它必须在为特定web应用程序定义的过滤器中是唯一的.
 96      */
 97     private String filterName = null;
 98
 99     public String getFilterName() {
100         return (this.filterName);
101     }
102
103     public void setFilterName(String filterName) {
104         this.filterName = filterName;
105     }
106
107     /**
108      *
109      * (可选元素)与这个过滤器相关的大图标
110      */
111     private String largeIcon = null;
112
113     public String getLargeIcon() {
114         return (this.largeIcon);
115     }
116
117     public void setLargeIcon(String largeIcon) {
118         this.largeIcon = largeIcon;
119     }
120
121     /**
122      * 此过滤器的初始化参数集,由参数名键入。
123      */
124     private Map parameters = new HashMap();
125
126     public Map getParameterMap() {
127
128         return (this.parameters);
129
130     }
131
132     /**
133      * 与此过滤器相关联的小图标.
134      */
135     private String smallIcon = null;
136
137     public String getSmallIcon() {
138         return (this.smallIcon);
139     }
140
141     public void setSmallIcon(String smallIcon) {
142         this.smallIcon = smallIcon;
143     }
144
145     // --------------------------------------------------------- Public Methods
146
147     /**
148      * 向与此过滤器关联的参数集添加初始化参数
149      *
150      * @param name
151      *            初始化参数的 name
152      * @param value
153      *            初始化参数的 value
154      */
155     public void addInitParameter(String name, String value) {
156
157         parameters.put(name, value);
158
159     }
160
161     public String toString() {
162
163         StringBuffer sb = new StringBuffer("FilterDef[");
164         sb.append("filterName=");
165         sb.append(this.filterName);
166         sb.append(", filterClass=");
167         sb.append(this.filterClass);
168         sb.append("]");
169         return (sb.toString());
170
171     }
172
173 }

然后这里在啰嗦几句关于上面的过滤器定义类,FilterDef类中的每一个属性表示在定义filter元素时生命的子元素,上面有写哈,其中Map类型的变量 parameters存储了初始化过滤器时所需要的所有参数,addInitparameter方法用于向parameters中添加新的name/value形式的参数名和对应的值。

FilterMap

org.apache.catalina.deploy.FilterMap类 是 我们常常用的 <filter-mapping>元素的定义组件 这个就不细说了 代码上我都写了注释,有兴趣的可以展开看下

  1 package org.apache.catalina.deploy;
  2
  3 import org.apache.catalina.util.RequestUtil;
  4
  5 /**
  6  *
  7  * <p>
  8  * <b>Title:FilterMap.java</b>
  9  * </p>
 10  * <p>
 11  * Copyright:ChenDong 2018
 12  * </p>
 13  * <p>
 14  * Company:仅学习时使用
 15  * </p>
 16  * <p>
 17  * 类功能描述:Web应用程序的筛选器映射的表示,如部署描述符中的<code>&lt;filter-mapping&gt;</code>元素所示。
 18  * 每个筛选器映射必须包含筛选器名称加上URL模式或servlet名称。
 19  * </p>
 20  * <p>
 21  * 介绍一下 <code>&lt;filter-mapping&gt;</code>元素包含的几个子元素
 22  * </p>
 23  * <p>
 24  * {@code filter-name } 必需, 该元素 必需与{@code <filter>}元素中声明的匹配
 25  * </p>
 26  * <p>
 27  * {@code url-pattern} 必需,该元素 声明了一个以/或者以*开始的匹配模式,这个模式指定了过滤器所应用的URL,但一个元素内只能配置一个
 28  * 若希望该过滤器应用于 多个 URL匹配模式 则应该提供多个 <code>&lt;filter-mapping&gt;</code>元素
 29  * </p>
 30  * <p>
 31  * {@code servlet-name} 这个是搭配使用的 但是如果使用 就必须有值,该元素给出的名称必须匹配Servlet或JSP页面的名称,
 32  * </p>
 33  *
 34  * @author 陈东
 35  * @date 2018年12月2日 下午3:03:29
 36  * @version 1.0
 37  */
 38 public final class FilterMap {
 39
 40     // ------------------------------------------------------------- Properties
 41
 42     /**
 43      * 当该映射与特定请求匹配时要执行的过滤器的名称
 44      */
 45     private String filterName = null;
 46
 47     public String getFilterName() {
 48         return (this.filterName);
 49     }
 50
 51     public void setFilterName(String filterName) {
 52         this.filterName = filterName;
 53     }
 54
 55     /**
 56      * 此映射匹配的servlet名称。
 57      */
 58     private String servletName = null;
 59
 60     public String getServletName() {
 61         return (this.servletName);
 62     }
 63
 64     public void setServletName(String servletName) {
 65         this.servletName = servletName;
 66     }
 67
 68     /**
 69      * 此映射匹配的URL模式。
 70      */
 71     private String urlPattern = null;
 72
 73     public String getURLPattern() {
 74         return (this.urlPattern);
 75     }
 76
 77     public void setURLPattern(String urlPattern) {
 78         this.urlPattern = RequestUtil.URLDecode(urlPattern);
 79     }
 80
 81     // --------------------------------------------------------- Public Methods
 82
 83     /**
 84      * Render a String representation of this object.
 85      */
 86     public String toString() {
 87
 88         StringBuffer sb = new StringBuffer("FilterMap[");
 89         sb.append("filterName=");
 90         sb.append(this.filterName);
 91         if (servletName != null) {
 92             sb.append(", servletName=");
 93             sb.append(servletName);
 94         }
 95         if (urlPattern != null) {
 96             sb.append(", urlPattern=");
 97             sb.append(urlPattern);
 98         }
 99         sb.append("]");
100         return (sb.toString());
101
102     }
103
104 }

FilterMap

ApplicationFilterConfig类

org.apache.catalina.core.ApplicationFilterConfig类实现了 javax.servlet.FilterConfig接口,ApplicationFilterConfig类用于管理Web应用程序第一次启动时,创建的所有的过滤器实例。

可以通过把一个 org.apcahe.catalina.Contex对象和 一个FilterDef对象传递给ApplicationFilterConfig的构造函数来创建一个ApplicationFilterConfig对象

 1     /**
 2      * 可以通过把一个{@link Context}对象 和一个 {@link FilterDef}对象传递给该构造函数,来创建一个新的
 3      * {@link ApplicationFilterConfig}实例
 4      *
 5      * @param context
 6      *            关联的Context
 7      * @param filterDef
 8      *            要构造{@code ApplicationFilterConfig}的过滤器定义
 9      *
10      * @exception ClassCastException
11      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类 没有实现
12      *                <code>javax.servlet.Filter</code> 接口,抛出该异常。
13      *
14      * @exception ClassNotFoundException
15      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类
16      *                没有被加载器找到,抛出异常 。
17      * @exception IllegalAccessException
18      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类
19      *                不能公开实例化,抛出异常 。
20      * @exception InstantiationException
21      *                如果实例化{@code Filter}对象时发生异常
22      * @exception ServletException
23      *                如果被过滤器的{@code init}方法抛出异常
24      */
25     public ApplicationFilterConfig(Context context, FilterDef filterDef) throws ClassCastException,
26             ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException {
27
28         super();
29         this.context = context;
30         setFilterDef(filterDef);
31
32     }

其中的Context表示的是一个Web应用程序,FilterDef对象表示一个过滤器的定义,

该类中使用一个 org.apcache.catalina,Context类型的 私有变量  context 来接收 构造函数 传入的 context对象

   /*** 与我们相关联的Context对象(代表一个Web应用程序)*/private Context context = null;

利用setFilterDef方法来设置 过滤器定义,且分配一个新的过滤器实例,下面看下方法定义

 1    /**
 2      *
 3      * 设置我们配置的 过滤器定义对象,并且还会有实例化该过滤器的功能
 4      *
 5      * @param filterDef
 6      *            新的过滤器定义
 7      *
 8      * @exception ClassCastException
 9      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类 没有实现
10      *                <code>javax.servlet.Filter</code> 接口,抛出该异常。
11      *
12      * @exception ClassNotFoundException
13      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类
14      *                没有被加载器找到,抛出异常 。
15      * @exception IllegalAccessException
16      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类
17      *                不能公开实例化,抛出异常 。
18      * @exception InstantiationException
19      *                如果实例化{@code Filter}对象时发生异常
20      * @exception ServletException
21      *                如果被过滤器的{@code init}方法抛出异常
22      */
23     void setFilterDef(FilterDef filterDef) throws ClassCastException, ClassNotFoundException, IllegalAccessException,
24             InstantiationException, ServletException {
25
26         this.filterDef = filterDef;
27         if (filterDef == null) {
28
29             // 释放任何先前分配的过滤器实例
30             if (this.filter != null)
31                 this.filter.destroy();
32             this.filter = null;
33
34         } else {
35
36             // 分配新的过滤器实例
37             Filter filter = getFilter();
38
39         }
40
41     }

ApplicationFilterConfig类的 getFilter方法会返回一个javax,servler.Filter对象,该方法负责载入并实例化一个过滤器

 1 /**
 2      * 返回已配置的应用程序过滤器器({@code javax.servlet.Filter}).
 3      *
 4      * @exception ClassCastException
 5      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类 没有实现
 6      *                <code>javax.servlet.Filter</code> 接口,抛出该异常。
 7      *
 8      * @exception ClassNotFoundException
 9      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类
10      *                没有被加载器找到,抛出异常 。
11      * @exception IllegalAccessException
12      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类
13      *                不能公开实例化,抛出异常 。
14      * @exception InstantiationException
15      *                如果实例化{@code Filter}对象时发生异常
16      * @exception ServletException
17      *                如果被过滤器的{@code init}方法抛出异常
18      */
19     Filter getFilter() throws ClassCastException, ClassNotFoundException, IllegalAccessException,
20             InstantiationException, ServletException {
21
22         // 返回现有的过滤器实例,如果有的话
23         if (this.filter != null)
24             return (this.filter);
25
26         // 标识我们将使用的类加载器 土话 就是 把我们想定义的Filter 的类的完全限定名 取出来
27         String filterClass = filterDef.getFilterClass();
28         ClassLoader classLoader = null;
29         // 如果 我们要加载的Filter 是 Catalina包下的类 我们不需要使用 Context容器中的loader
30         // 直接使用当前的系统类加载器就行了
31         // 因为 loader的加载器 是有资源访问限制的 这就不细说了
32         if (filterClass.startsWith("org.apache.catalina."))
33             classLoader = this.getClass().getClassLoader();
34         else
35             classLoader = context.getLoader().getClassLoader();
36
37         // 搞什么飞机
38         ClassLoader oldCtxClassLoader = Thread.currentThread().getContextClassLoader();
39
40         // 实例化此过滤器的一个新实例并返回它
41         Class clazz = classLoader.loadClass(filterClass);
42         this.filter = (Filter) clazz.newInstance();
43
44         filter.init(this);
45         return (this.filter);
46
47     }

下面直接把全部代码贴出来

  1 package org.apache.catalina.core;
  2
  3 import java.util.ArrayList;
  4 import java.util.Enumeration;
  5 import java.util.Map;
  6 import javax.servlet.Filter;
  7 import javax.servlet.FilterConfig;
  8 import javax.servlet.ServletContext;
  9 import javax.servlet.ServletException;
 10 import org.apache.catalina.Context;
 11 import org.apache.catalina.deploy.FilterDef;
 12 import org.apache.catalina.util.Enumerator;
 13
 14 /**
 15  *
 16  * <p>
 17  * <b>Title:ApplicationFilterConfig.java</b>
 18  * </p>
 19  * <p>
 20  * Copyright:ChenDong 2018
 21  * </p>
 22  * <p>
 23  * Company:仅学习时使用
 24  * </p>
 25  * <p>
 26  * 类功能描述:实现了<code>javax.servlet.FilterConfig</code>接口,
 27  * 用于管理Web应用程序第一次启动时创建的所有的过滤器实例
 28  * </p>
 29  *
 30  * @author 陈东
 31  * @date 2018年12月1日 下午10:05:41
 32  * @version 1.0
 33  */
 34 final class ApplicationFilterConfig implements FilterConfig {
 35
 36     // ----------------------------------------------------------- Constructors
 37
 38     /**
 39      * 可以通过把一个{@link Context}对象 和一个 {@link FilterDef}对象传递给该构造函数,来创建一个新的
 40      * {@link ApplicationFilterConfig}实例
 41      *
 42      * @param context
 43      *            关联的Context(表示一个Web应用程序)
 44      * @param filterDef
 45      *            要构造{@code ApplicationFilterConfig}的过滤器定义
 46      *
 47      * @exception ClassCastException
 48      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类 没有实现
 49      *                <code>javax.servlet.Filter</code> 接口,抛出该异常。
 50      *
 51      * @exception ClassNotFoundException
 52      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类
 53      *                没有被加载器找到,抛出异常 。
 54      * @exception IllegalAccessException
 55      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类
 56      *                不能公开实例化,抛出异常 。
 57      * @exception InstantiationException
 58      *                如果实例化{@code Filter}对象时发生异常
 59      * @exception ServletException
 60      *                如果被过滤器的{@code init}方法抛出异常
 61      */
 62     public ApplicationFilterConfig(Context context, FilterDef filterDef) throws ClassCastException,
 63             ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException {
 64
 65         super();
 66         this.context = context;
 67         setFilterDef(filterDef);
 68
 69     }
 70
 71     // ----------------------------------------------------- Instance Variables
 72
 73     /**
 74      * 与我们相关联的Context对象(代表一个Web应用程序)
 75      */
 76     private Context context = null;
 77
 78     /**
 79      * 我们配置的应用程序过滤器.
 80      */
 81     private Filter filter = null;
 82
 83     /**
 84      * 我们配置的应用程序过滤器的<code>FilterDef</code>定义(Filter 是过滤器实例
 85      * 而<code>FilterDef</code>是定义过滤器的对象)
 86      */
 87     private FilterDef filterDef = null;
 88
 89     // --------------------------------------------------- FilterConfig Methods
 90
 91     /**
 92      * 返回我们配置的应用程序 过滤器的名字
 93      */
 94     public String getFilterName() {
 95
 96         return (filterDef.getFilterName());
 97
 98     }
 99
100     /**
101      * 返回包含命名初始化参数的<code>String</code>类型的值,如果参数不存在,返回<code>null</code>。
102      *
103      * @param name
104      *            请求返回的初始化参数的名称
105      */
106     public String getInitParameter(String name) {
107
108         Map map = filterDef.getParameterMap();
109         if (map == null)
110             return (null);
111         else
112             return ((String) map.get(name));
113
114     }
115
116     /**
117      * 返回此筛选器的初始化参数名称的<code>Enumeration</code>。
118      */
119     @SuppressWarnings("rawtypes")
120     public Enumeration getInitParameterNames() {
121
122         Map map = filterDef.getParameterMap();
123         if (map == null)
124             return (new Enumerator(new ArrayList()));
125         else
126             return (new Enumerator(map.keySet()));
127
128     }
129
130     /**
131      *
132      * 返回相关Web应用程序的{@link ServletContext}。
133      */
134     public ServletContext getServletContext() {
135
136         return (this.context.getServletContext());
137
138     }
139
140     public String toString() {
141
142         StringBuffer sb = new StringBuffer("ApplicationFilterConfig[");
143         sb.append("name=");
144         sb.append(filterDef.getFilterName());
145         sb.append(", filterClass=");
146         sb.append(filterDef.getFilterClass());
147         sb.append("]");
148         return (sb.toString());
149
150     }
151
152     // -------------------------------------------------------- Package Methods
153
154     /**
155      * 返回已配置的应用程序过滤器器({@code javax.servlet.Filter}).
156      *
157      * @exception ClassCastException
158      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类 没有实现
159      *                <code>javax.servlet.Filter</code> 接口,抛出该异常。
160      *
161      * @exception ClassNotFoundException
162      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类
163      *                没有被加载器找到,抛出异常 。
164      * @exception IllegalAccessException
165      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类
166      *                不能公开实例化,抛出异常 。
167      * @exception InstantiationException
168      *                如果实例化{@code Filter}对象时发生异常
169      * @exception ServletException
170      *                如果被过滤器的{@code init}方法抛出异常
171      */
172     Filter getFilter() throws ClassCastException, ClassNotFoundException, IllegalAccessException,
173             InstantiationException, ServletException {
174
175         // 返回现有的过滤器实例,如果有的话
176         if (this.filter != null)
177             return (this.filter);
178
179         // 标识我们将使用的类加载器 土话 就是 把我们想定义的Filter 的类的完全限定名 取出来
180         String filterClass = filterDef.getFilterClass();
181         ClassLoader classLoader = null;
182         // 如果 我们要加载的Filter 是 Catalina包下的类 我们不需要使用 Context容器中的loader
183         // 直接使用当前的系统类加载器就行了
184         // 因为 loader的加载器 是有资源访问限制的 这就不细说了
185         if (filterClass.startsWith("org.apache.catalina."))
186             classLoader = this.getClass().getClassLoader();
187         else
188             classLoader = context.getLoader().getClassLoader();
189
190         // 搞什么飞机
191         ClassLoader oldCtxClassLoader = Thread.currentThread().getContextClassLoader();
192
193         // 实例化此过滤器的一个新实例并返回它
194         Class clazz = classLoader.loadClass(filterClass);
195         this.filter = (Filter) clazz.newInstance();
196
197         filter.init(this);
198         return (this.filter);
199
200     }
201
202     /**
203      * 返回我们配置的 过滤器定义类 {@link FilterDef}
204      */
205     FilterDef getFilterDef() {
206
207         return (this.filterDef);
208
209     }
210
211     /**
212      *
213      * 如果存在,则释放与此{@link FilterConfig}关联的过滤器实例。
214      */
215     void release() {
216
217         if (this.filter != null)
218             filter.destroy();
219         this.filter = null;
220
221     }
222
223     /**
224      *
225      * 设置我们配置的 过滤器定义对象,并且还会有实例化该过滤器的功能
226      *
227      * @param filterDef
228      *            新的过滤器定义
229      *
230      * @exception ClassCastException
231      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类 没有实现
232      *                <code>javax.servlet.Filter</code> 接口,抛出该异常。
233      *
234      * @exception ClassNotFoundException
235      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类
236      *                没有被加载器找到,抛出异常 。
237      * @exception IllegalAccessException
238      *                如果{@code FilterDef}中的 {@code filterClass}变量指定的类
239      *                不能公开实例化,抛出异常 。
240      * @exception InstantiationException
241      *                如果实例化{@code Filter}对象时发生异常
242      * @exception ServletException
243      *                如果被过滤器的{@code init}方法抛出异常
244      */
245     void setFilterDef(FilterDef filterDef) throws ClassCastException, ClassNotFoundException, IllegalAccessException,
246             InstantiationException, ServletException {
247
248         this.filterDef = filterDef;
249         if (filterDef == null) {
250
251             // 释放任何先前分配的过滤器实例
252             if (this.filter != null)
253                 this.filter.destroy();
254             this.filter = null;
255
256         } else {
257
258             // 分配新的过滤器实例
259             Filter filter = getFilter();
260
261         }
262
263     }
264
265     // -------------------------------------------------------- Private Methods
266
267 }

ApplicationFilterConfig的类定义

ApplicationFilterChain

过滤器链的定义

org.apache.catalina.core.ApplicationFilterChain类实现了 javax.servlert.FilterChain接口,StandardWrapperValue类的 invoke方法会创建ApplicationFliterChain类的一个实例,并调用其doFilter方法,ApplicationFilterChain类的doFilter方法会调用过滤器链中的第一个过滤器的doFilter方法,Filter接口的doFilter方法的签名是

1 void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;

这个熟悉 Container容器的管道的同学可能一看就看出来这个其实 和 管道的 ValveContext 对象的 invoke方法类似,目的都是把管道或者链中的所有对象都执行一遍,

那个可能有不熟悉的同学 这里就简单的说下:ApplicationFilterChain类的doFilter方法 会将 ApplicationFilterChain自身作为第三个参数传给过滤器的doFilter方法,在过滤器的doFilter中,可以显示的通过调用FilterChain对象的doFilter方法来调用另一个过滤器,如果某个过滤器时过滤器链中的最后一个过滤器,则会调用被请求的Servlet类的service方法,如果过滤器没有调用chain.doFilter方法,则不会调用后面的过滤器。

展示一下 Filter中的 doFilter方法的伪代码

void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException{/******** 想干啥干啥*********/var3.doFilter(var1,var2);}

然后同样把全部代码粘贴出来有兴趣可以看下

  1 package org.apache.catalina.core;
  2
  3 import java.io.IOException;
  4 import java.util.ArrayList;
  5 import java.util.Iterator;
  6 import java.security.PrivilegedActionException;
  7 import javax.servlet.Filter;
  8 import javax.servlet.FilterChain;
  9 import javax.servlet.Servlet;
 10 import javax.servlet.ServletException;
 11 import javax.servlet.ServletRequest;
 12 import javax.servlet.ServletResponse;
 13 import javax.servlet.http.HttpServletRequest;
 14 import javax.servlet.http.HttpServletResponse;
 15 import org.apache.catalina.InstanceEvent;
 16 import org.apache.catalina.util.InstanceSupport;
 17 import org.apache.catalina.util.StringManager;
 18
 19 /**
 20  *
 21  * <p>
 22  * <b>Title:ApplicationFilterChain.java</b>
 23  * </p>
 24  * <p>
 25  * Copyright:ChenDong 2018
 26  * </p>
 27  * <p>
 28  * Company:仅学习时使用
 29  * </p>
 30  * <p>
 31  * 类功能描述: 用于管理特定请求的一组过滤器执行链
 32  * 是<code>javax.servlet.FilterChain</code>的实现。当已定义的过滤器集全部执行完毕时,对
 33  * <code>doFilter()</code> 的下一次调用将执行servlet的<code>service()</code>方法。
 34  * </p>
 35  *
 36  * @author 陈东
 37  * @date 2018年12月2日 上午10:47:54
 38  * @version 1.0
 39  */
 40 final class ApplicationFilterChain implements FilterChain {
 41
 42     // ----------------------------------------------------------- Constructors
 43
 44     /**
 45      * 构造一个没有定义过滤器的新链实例
 46      */
 47     public ApplicationFilterChain() {
 48
 49         super();
 50
 51     }
 52
 53     // ----------------------------------------------------- Instance Variables
 54
 55     /**
 56      * 将在这个链上执行的一组过滤器。
 57      */
 58     private ArrayList filters = new ArrayList();
 59
 60     /**
 61      *
 62      * 用于保持过滤器链中当前位置的迭代器。这个迭代器被称为第一次调用<code>doFilter()</code>
 63      */
 64     private Iterator iterator = null;
 65
 66     /**
 67      * 要由这个链执行的servlet实例.
 68      */
 69     private Servlet servlet = null;
 70
 71     /**
 72      * StringManger工具类.
 73      */
 74     private static final StringManager sm = StringManager.getManager(Constants.Package);
 75
 76     /**
 77      *
 78      * 与Wrapper关联的InstanceSupport实例(用于发送“before filter”和“after filter”事件。
 79      */
 80     private InstanceSupport support = null;
 81
 82     // ---------------------------------------------------- FilterChain Methods
 83
 84     /**
 85      *
 86      * 调用该链中的下一个过滤器,传递指定的请求和响应。如果在此链中不再有过滤器,则调用servlet本身的 <code>service()</code>
 87      * 方法
 88      *
 89      * @param request
 90      *            我们正在处理的servlet请求
 91      * @param response
 92      *            我们正在创建的servlet响应
 93      *
 94      * @exception IOException
 95      *                如果发生输入/输出错误
 96      * @exception ServletException
 97      *                如果出现servlet异常
 98      */
 99     public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
100
101         // 如果设置了安全管理器
102         if (System.getSecurityManager() != null) {
103             final ServletRequest req = request;
104             final ServletResponse res = response;
105             try {
106                 java.security.AccessController.doPrivileged(new java.security.PrivilegedExceptionAction() {
107                     public Object run() throws ServletException, IOException {
108                         internalDoFilter(req, res);
109                         return null;
110                     }
111                 });
112             } catch (PrivilegedActionException pe) {
113                 Exception e = pe.getException();
114                 if (e instanceof ServletException)
115                     throw (ServletException) e;
116                 else if (e instanceof IOException)
117                     throw (IOException) e;
118                 else if (e instanceof RuntimeException)
119                     throw (RuntimeException) e;
120                 else
121                     throw new ServletException(e.getMessage(), e);
122             }
123         } else {
124             internalDoFilter(request, response);
125         }
126     }
127
128     /**
129      *
130      *
131      * <p>
132      * Title: internalDoFilter
133      * </p>
134      *
135      * @date 2018年12月2日 上午10:54:27
136      *
137      *       <p>
138      *       功能描述:真正干活的“doFilter”
139      *       </p>
140      *
141      * @param request
142      * @param response
143      * @throws IOException
144      * @throws ServletException
145      */
146     private void internalDoFilter(ServletRequest request, ServletResponse response)
147             throws IOException, ServletException {
148
149         // 第一次调用这个方法时构造 过滤器链的迭代器
150         if (this.iterator == null)
151             this.iterator = filters.iterator();
152
153         // 如果链中有一个过滤器,则调用下一个过滤器
154         if (this.iterator.hasNext()) {
155             // 真正的过滤器 是由 ApplicationFilterConfig 类 的getFilter方法实例化的
156             ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) iterator.next();
157             Filter filter = null;
158             try {
159                 filter = filterConfig.getFilter();
160                 // 触发BEFORE_FILTER_EVENT 事件
161                 support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT, filter, request, response);
162                 // 真正执行 doFilter方法
163                 filter.doFilter(request, response, this);
164                 // 触发AFTER_FILTER_EVENT事件
165                 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response);
166             } catch (IOException e) {
167                 if (filter != null)
168                     support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response, e);
169                 throw e;
170             } catch (ServletException e) {
171                 if (filter != null)
172                     support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response, e);
173                 throw e;
174             } catch (RuntimeException e) {
175                 if (filter != null)
176                     support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response, e);
177                 throw e;
178             } catch (Throwable e) {
179                 if (filter != null)
180                     support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response, e);
181                 throw new ServletException(sm.getString("filterChain.filter"), e);
182             }
183             return;
184         }
185         // 到这里呢 说明联中的过滤器已经执行完了 所以接下来要调用Servlet实例的 service方法了
186         try {
187             // 触发 BEFORE_SERVICE_EVENT事件
188             support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT, servlet, request, response);
189             // 真正调用 Servlet的service方法
190             if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) {
191                 servlet.service((HttpServletRequest) request, (HttpServletResponse) response);
192             } else {
193                 servlet.service(request, response);
194             }
195             // 触发AFTER_SERVICE_EVENT事件
196             support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response);
197         } catch (IOException e) {
198             support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response, e);
199             throw e;
200         } catch (ServletException e) {
201             support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response, e);
202             throw e;
203         } catch (RuntimeException e) {
204             support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response, e);
205             throw e;
206         } catch (Throwable e) {
207             support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response, e);
208             throw new ServletException(sm.getString("filterChain.servlet"), e);
209         }
210
211     }
212
213     // -------------------------------------------------------- Package Methods
214
215     /**
216      * 将一个过滤器添加到将在这个链中执行的过滤器集合中.
217      *
218      * @param filterConfig
219      *            要执行的servlet的过滤器
220      */
221     void addFilter(ApplicationFilterConfig filterConfig) {
222
223         this.filters.add(filterConfig);
224
225     }
226
227     /**
228      * 释放对该链执行的过滤器和包装器的引用。
229      */
230     void release() {
231
232         this.filters.clear();
233         this.iterator = iterator;
234         this.servlet = null;
235
236     }
237
238     /**
239      * 置将在该链的末尾执行的servlet
240      *
241      * @param servlet
242      *            执行的servlet
243      */
244     void setServlet(Servlet servlet) {
245
246         this.servlet = servlet;
247
248     }
249
250     /**
251      * 设置用于此过滤器链的事件通知的{@code InstanceSupport}对象。
252      *
253      * @param support
254      *            InstanceSupport对象
255      */
256     void setSupport(InstanceSupport support) {
257
258         this.support = support;
259
260     }
261
262 }

ApplicationFilterChain

StandardWrapperValue

StandardWrapperValue类 是StandardWrapper实例中的基础阀,它主要完成两个操作;

  1. 执行与该Servlet实例关联的全部过滤器
  2. 调用Servlet实例的service方法

为了完成上述任务,在StandardWrapperVlaue类的 invoke方法中会执行以下几个操作,

  1. 调用StandardWrapper实例的allocate方法获取该StandardWrapper实例所表示的Servlet类实例。
  2. 调用私有方法 createFilterChain 创建过滤器链
  3. 调用过滤器链的 doFilter方法,其中包括调用Servlet实例的service方法,
  4. 释放过滤器
  5. 调用Wrapper实例的deallocate方法;
  6. 过该Servlet类再也不会被使用到,则调用Wrapper实例的unload方法

展示一下 invoke方法的部分代码

第一步 调用StandardWrapper类的allocate方法来分配一个Servlet实例

 1 // 分配servlet实例来处理此请求
 2         try {
 3             if (!unavailable) {
 4                 // 第一操作 调用StandardWrapper类的allocate方法 分配一个Servlet实例
 5                 servlet = wrapper.allocate();
 6             }
 7         } catch (ServletException e) {
 8             log(sm.getString("standardWrapper.allocateException", wrapper.getName()), e);
 9             throwable = e;
10             exception(request, response, e);
11             servlet = null;
12         } catch (Throwable e) {
13             log(sm.getString("standardWrapper.allocateException", wrapper.getName()), e);
14             throwable = e;
15             exception(request, response, e);
16             servlet = null;
17         }

第二步

创建过滤器链

// 第二步:调用私有方法 createFilterChain 来创建过滤器链ApplicationFilterChain filterChain = createFilterChain(request, servlet);

再展示下 createFilterChain的方法代码

 1 /**
 2      *
 3      * 构造并返回一个FilterChain实现,它将包装指定servlet实例的执行。如果我们根本不执行一个过滤链,返回
 4      * <code>null</code>。
 5      * <p>
 6      * <strong>FIXME</strong> - 链实例!
 7      *
 8      * @param request
 9      *            我们正在处理的servlet请求
10      * @param servlet
11      *            要被包装的Servlet实例
12      */
13     private ApplicationFilterChain createFilterChain(Request request, Servlet servlet) {
14
15         // 如果没有servlet执行,返回null
16         if (servlet == null)
17             return (null);
18
19         // 创建并初始化过滤器链对象
20         ApplicationFilterChain filterChain = new ApplicationFilterChain();
21         filterChain.setServlet(servlet);
22         StandardWrapper wrapper = (StandardWrapper) getContainer();
23         filterChain.setSupport(wrapper.getInstanceSupport());
24
25         // 获取此Context的过滤器映射
26         StandardContext context = (StandardContext) wrapper.getParent();
27         // 这个过滤器映射集合怎么得来的 根据 web描述文件中的 <filter -mapping>
28         FilterMap filterMaps[] = context.findFilterMaps();
29
30         // 如果没有过滤器映射,我们就完成了
31         if ((filterMaps == null) || (filterMaps.length == 0))
32             return (filterChain);
33
34         // 获取匹配过滤器映射所需的信息
35         String requestPath = null;
36         if (request instanceof HttpRequest) {
37             HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
38             String contextPath = hreq.getContextPath();
39             if (contextPath == null)
40                 contextPath = "";
41             String requestURI = ((HttpRequest) request).getDecodedRequestURI();
42             if (requestURI.length() >= contextPath.length())
43                 requestPath = requestURI.substring(contextPath.length());
44         }
45         String servletName = wrapper.getName();
46
47         int n = 0;
48
49         // 将匹配到相关路径映射的过滤器添加到该过滤器链
50         for (int i = 0; i < filterMaps.length; i++) {
51
52             if (!matchFiltersURL(filterMaps[i], requestPath))
53                 continue;
54             ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context
55                     .findFilterConfig(filterMaps[i].getFilterName());
56             if (filterConfig == null) {
57
58                 continue;
59             }
60             filterChain.addFilter(filterConfig);
61             n++;
62         }
63
64         // 添加与Servlet名称匹配的过滤器
65         for (int i = 0; i < filterMaps.length; i++) {
66
67             if (!matchFiltersServlet(filterMaps[i], servletName))
68                 continue;
69             ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context
70                     .findFilterConfig(filterMaps[i].getFilterName());
71             if (filterConfig == null) {
72                 continue;
73             }
74             filterChain.addFilter(filterConfig);
75             n++;
76         }
77
78         // 返回过滤器链
79         return (filterChain);
80
81     }

第三步 执行过滤器的doFilter方法 ,在其链的doFilter方法最后 会调用 Servlet的service方法 可以参见上面的ApplicationFilterChain类

    if ((servlet != null) && (filterChain != null)) {// 第三步:调用过滤器的 doFilter方法 包括调用Servlet的service方法
                filterChain.doFilter(sreq, sres);}

第四步 释放过滤器链

 1 try {
 2             if (filterChain != null)
 3                 // 第四步 释放过滤器链
 4                 filterChain.release();
 5         } catch (Throwable e) {
 6             log(sm.getString("standardWrapper.releaseFilters", wrapper.getName()), e);
 7             if (throwable == null) {
 8                 throwable = e;
 9                 exception(request, response, e);
10             }
11         }

第五步 调用Wrapper的deallocate方法 将Servlet实例还给 StandardWrapper

 1 // 第五步 调用Wrapper实例的 deallocate方法
 2         try {
 3             if (servlet != null) {
 4
 5                 wrapper.deallocate(servlet);
 6             }
 7         } catch (Throwable e) {
 8             log(sm.getString("standardWrapper.deallocateException", wrapper.getName()), e);
 9             if (throwable == null) {
10                 throwable = e;
11                 exception(request, response, e);
12             }
13         }

在给大家展示一下StandardWrapper类的dellocate方法

 1 /**
 2      *
 3      * 将先前分配的servlet返回到可用实例池中。如果此servlet类不实现{@link SingleThreadModel}接口,
 4      * 则实际上不需要任何操作
 5      *
 6      * @param servlet
 7      *            被返回来的Servlet实例
 8      *
 9      * @exception ServletException
10      *                如果发生了分配错误
11      */
12     public void deallocate(Servlet servlet) throws ServletException {
13
14         // 如果此servlet类不实现{@link SingleThreadModel}接口,只需要把当前活动的Servlet实例计数减一 就可以了
15         if (!singleThreadModel) {
16             countAllocated--;
17             return;
18         }
19
20         // 解锁并释放此实例
21         synchronized (instancePool) {
22             countAllocated--;
23             instancePool.push(servlet);
24             instancePool.notify();
25         }
26
27     }

第六步 若该Servlet类在也不会被使用到,则调用Wrapper实例的unload方法

说这个方法之前 现引入一个StandardWrapper类的表示变量

   /*** 这个servlet可用的日期和时间(自纪元以来以毫秒为单位),或者如果servlet可用,则为零。如果此值等于.{@code MAX_VALUE}* 则此servlet的不可用性被认为是永久性的。*/private long available = 0L

这个变量是可以通过下面方法设置的

 1 /**
 2      * 设置该servlet的可用日期/时间,从该纪元开始,以毫秒为单位。如果此日期/时间在将来,
 3      * 则对该servlet的任何请求都将返回{@code SC_SERVICE_UNAVAILABLE}错误
 4      *
 5      * @param available
 6      *            新的可用日期/时间
 7      */
 8     public void setAvailable(long available) {
 9
10         long oldAvailable = this.available;
11         if (available > System.currentTimeMillis())
12             this.available = available;
13         else
14             this.available = 0L;
15         support.firePropertyChange("available", new Long(oldAvailable), new Long(this.available));
16
17     }

那么我们回过头来看  第六步 如检查到 上面的表示 为 MAX_VALUE,就会执行 wrapper 的unload方法

try {if ((servlet != null) && (wrapper.getAvailable() == Long.MAX_VALUE)) {wrapper.unload();}} catch (Throwable e) {log(sm.getString("standardWrapper.unloadException", wrapper.getName()), e);if (throwable == null) {throwable = e;exception(request, response, e);}}

看下面 展示 unload方法

 1 /**
 2      * 在调用每个实例的<code>destroy()</code>方法后,卸载此servlet的所有初始化实例。例如,
 3      * 可以在关闭整个servlet引擎之前, 或者在从与Loader的存储库关联的加载程序中重新加载所有类之前
 4      *
 5      * @exception ServletException
 6      *                if an exception is thrown by the destroy() method
 7      */
 8     public synchronized void unload() throws ServletException {
 9
10         // 如果我们从来没有加载过实例
11         if (!singleThreadModel && (instance == null))
12             return;
13         unloading = true;
14
15         // 如果当前实例已分配,则暂时停留
16         // (可能超过一个,如果非STM)
17         if (countAllocated > 0) {
18             int nRetries = 0;
19             while (nRetries < 10) {
20                 if (nRetries == 0) {
21                     log("等待" + countAllocated + " 实例被解除分配 ");
22                 }
23                 try {
24                     Thread.sleep(50);
25                 } catch (InterruptedException e) {
26                     ;
27                 }
28                 nRetries++;
29             }
30         }
31
32         // 获取当前线程中的加载器
33         ClassLoader oldCtxClassLoader = Thread.currentThread().getContextClassLoader();
34         // 获取当初实例化 Instance时 的加载器
35         ClassLoader classLoader = instance.getClass().getClassLoader();
36
37         PrintStream out = System.out;
38         SystemLogHandler.startCapture();
39
40         // 调用Servlet 的destory()方法
41         try {
42             instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_DESTROY_EVENT, instance);
43             Thread.currentThread().setContextClassLoader(classLoader);
44             instance.destroy();
45             instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_DESTROY_EVENT, instance);
46         } catch (Throwable t) {
47             instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_DESTROY_EVENT, instance, t);
48             instance = null;
49             instancePool = null;
50             nInstances = 0;
51             fireContainerEvent("unload", this);
52             unloading = false;
53             throw new ServletException(sm.getString("standardWrapper.destroyException", getName()), t);
54         } finally {
55             // 将上面存起来的线程加载器 重置回去
56             Thread.currentThread().setContextClassLoader(oldCtxClassLoader);
57             // Write captured output
58             String log = SystemLogHandler.stopCapture();
59             if (log != null && log.length() > 0) {
60                 if (getServletContext() != null) {
61                     getServletContext().log(log);
62                 } else {
63                     out.println(log);
64                 }
65             }
66         }
67
68         // 撤销被销毁的实例
69         instance = null;
70
71         if (singleThreadModel && (instancePool != null)) {
72             try {
73                 Thread.currentThread().setContextClassLoader(classLoader);
74                 // 这里是将 栈顶元素的元素取出进行销毁(因为StandardWrapperValue的invoke方法会先将
75                 // Servlet换回来)
76                 // 这里 为什么要把 STM池中的Servlet实例都销毁呢?大家不要忘记 哈 实质上N个STM
77                 // 实例全都是代表一个Servlet的 当初是为了提升性能才实例了多个,所以要全部销毁
78                 while (!instancePool.isEmpty()) {
79                     ((Servlet) instancePool.pop()).destroy();
80                 }
81             } catch (Throwable t) {
82                 instancePool = null;
83                 nInstances = 0;
84                 unloading = false;
85                 fireContainerEvent("unload", this);
86                 throw new ServletException(sm.getString("standardWrapper.destroyException", getName()), t);
87             } finally {
88                 // restore the context ClassLoader
89                 Thread.currentThread().setContextClassLoader(oldCtxClassLoader);
90             }
91             instancePool = null;
92             nInstances = 0;
93         }
94
95         unloading = false;
96         fireContainerEvent("unload", this);
97
98     }

那么目前为止 关于 tomcat 的Servlet容器之一 标准实现的StandardWrapper 类的相关功能 就说完了 下一篇文章将会 展示 另一个 Servlet容器  Context的标准实现 StandardContext

转载于:https://www.cnblogs.com/ChenD/p/Tomcat-StandardWrapper-Wrapper.html

StandardWrapper相关推荐

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

    [0]README 0.0)本文部分文字描述转自 "how tomcat works",旨在学习 "tomcat(11)StandardWrapper源码剖析" ...

  2. StandardWrapper ...$$EnhancerByCGLIB$$b9

    没有实现变量的set与get方法 转载于:https://www.cnblogs.com/slowly-keeping/p/3447765.html

  3. How Tomcat works — 四、tomcat启动(3)

    上一节说到StandardService负责启动其子组件:container和connector,不过注意,是有先后顺序的,先启动container,再启动connector,这一节先来看看conta ...

  4. Tomcat 架构原理解析到架构设计借鉴

    ‍ 点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 Tomcat 架构原理解析到架构设计借鉴 Tomcat 发展这 ...

  5. java nio 写事件_Java NIO

    java Nio Selector 选择器 Buffer 缓冲器 Channel 通道 Selector是NIO的核心,是channel的管理者,通过执行select()阻塞方式,监听是否有chann ...

  6. 牛逼!硬核图解 Tomcat 整体架构

    总体架构 核心功能: 处理 socket 连接,负责将网络字节流与 Request 和 Response 对象的转化: 加载和管理 Servlet,以及具体处理 Request 请求: Tomcat ...

  7. expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.spring

    在Spring项目中自动想用注解的方式,在controller里面注入Service但是报错,错误信息如下: [WARNING] Exception encountered during contex ...

  8. [Spring mvc 深度解析(三)] 创建Spring MVC之器

    第9章 创建Spring MVC之器 ​ 本章将分析Spring MVC自身的创建过程.首先分析Spring MVC的整体结构,然后具体分析每一层的创建过程. 1 整体结构介绍 Spring MVC中 ...

  9. [Spring mvc 深度解析(二)] Tomcat分析

    第7章 Tomcat分析 ​ 前面已经给大家介绍了网站处理请求时所涉及的各种协议和实现方法,不过之前的实现只是为了让大家明白原理而设计的简单示例程序,本章分析一个实际环境中经常使用的具体的实现--To ...

最新文章

  1. PCL:点云配准1、基础知识:平面3自由度、旋转矩阵精讲
  2. Oracle中数据导出成CVS,EXCEL
  3. js(function(){alert(‘’‘)})
  4. Git图形化管理工具
  5. 【CASS精品教程】CASS9.1土方量的计算方法汇总
  6. 如何从JavaScript数组中获取多个随机唯一元素?
  7. [Linux]Linux应用程序中添加强制中断处理
  8. LeetCode 1275. 找出井字棋的获胜者(位运算)
  9. 波士顿大学研究生计算机科学专业排名,2020年波士顿大学排名TFE Times美国最佳计算机科学硕士专业排名第52...
  10. 在单位用oracle备份到磁带的脚本(看不明白的地方交流)
  11. 《Kotlin从零到精通Android开发》资源下载和内容勘误
  12. linux 开放端口
  13. 使用 Python 进行科学计算 使用 Python 进行科学计算
  14. 基于链队列的银行叫号系统
  15. 交换机芯片笔记2.1
  16. docker学习--数据卷
  17. 拼多多商品发布规则|一度智信
  18. 【java实现二维码的生成(源码)】
  19. 从零开始学习Java神经网络、自然语言处理和语音识别,附详解和简易版GPT,语音识别完整代码示例解析
  20. 中国智能制造的务实落地方案

热门文章

  1. python职业发展规划书范文_职业生涯规划书范文 3篇
  2. 字典 选取前100_100道 Python 经典练习题004
  3. SQL SERVER 2000 数据库备份和SQL Server数据库备份有两种方式,
  4. 10蓝牙_小米10手机专用?小米“真无线蓝牙耳机Air 2s”评测
  5. python bokeh slider_Bokeh数据可视化工具3视觉增强及服务器
  6. channelinboundhandler中都包含了哪一类的方法_标准气体的分类类别与相关气体配置方式方法...
  7. android webview内存泄漏,Android由webview引起的内存泄漏
  8. 图像融合(三)-- 拉普拉斯金字塔
  9. 图像局部特征(三)--FAST角点检测子
  10. mysql 组复制和传统复制_2017年12月聚合文章--MySQL 传统复制中常见故障处理和结构优化案例分析 | 码友网...