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方法的第二部分
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.
意思大概:
- load-on-startup 元素标记容器是否应该在web应用程序启动的时候就加载这个servlet,(实例化并调用其init()方法)。
- 它的值必须是一个整数,表示servlet被加载的先后顺序。
- 如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载。
- 如果值为正整数或者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个方法;
- getServletContext()
- getServletName()
- getInitParameter
- 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><filter-mapping></code>元素所示。 18 * 每个筛选器映射必须包含筛选器名称加上URL模式或servlet名称。 19 * </p> 20 * <p> 21 * 介绍一下 <code><filter-mapping></code>元素包含的几个子元素 22 * </p> 23 * <p> 24 * {@code filter-name } 必需, 该元素 必需与{@code <filter>}元素中声明的匹配 25 * </p> 26 * <p> 27 * {@code url-pattern} 必需,该元素 声明了一个以/或者以*开始的匹配模式,这个模式指定了过滤器所应用的URL,但一个元素内只能配置一个 28 * 若希望该过滤器应用于 多个 URL匹配模式 则应该提供多个 <code><filter-mapping></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 }
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 }
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 }
StandardWrapperValue
StandardWrapperValue类 是StandardWrapper实例中的基础阀,它主要完成两个操作;
- 执行与该Servlet实例关联的全部过滤器
- 调用Servlet实例的service方法
为了完成上述任务,在StandardWrapperVlaue类的 invoke方法中会执行以下几个操作,
- 调用StandardWrapper实例的allocate方法获取该StandardWrapper实例所表示的Servlet类实例。
- 调用私有方法 createFilterChain 创建过滤器链
- 调用过滤器链的 doFilter方法,其中包括调用Servlet实例的service方法,
- 释放过滤器
- 调用Wrapper实例的deallocate方法;
- 过该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