tomcat(11)org.apache.catalina.core.StandardWrapper源码剖析
【1】方法调用序列
step1)连接器创建 request 和 response对象;step2)连接器调用StandardContext.invoke()方法;step3)StandardContext.invoke()方法调用其管道的invoke() 方法。StandardContext的管道对象的基础阀是 StandardCoantextValve类的实例,因此, StandardContext 的管道会调用 StandardContextValve.invoke()方法;step4)StandardContextValve.invoke()方法 获取相应的Wrapper 实例处理 http请求,调用Wrapper实例的invoke()方法;step5)StandardWrapper类是Wrapper接口的标准实现,StandardWrapper.invoke()方法 会调用其管道对象的invoke()方法;step6)StandardWrapper的管道对象中的基础阀是 StandardWrapperValve 类的实例,因此,会调用StandardWrapperValve.invoke()方法,StandardWrapperValve.invoke()方法会调用Wrapper实例的 allocate() 方法获取servlet实例;step7)allocate()方法调用load() 方法载入相应的servlet类,若已经载入,则无需重复载入;step8)load()方法调用servlet实例的init()方法;step9)StandardWrapperValve调用servlet.service()方法;// Call the filter chain for this request// NOTE: This also calls the servlet's service() methodtry { // org.apache.catalina.core.StandardWrapperValve.invoke()String jspFile = wrapper.getJspFile();if (jspFile != null)sreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile);elsesreq.removeAttribute(Globals.JSP_FILE_ATTR);if ((servlet != null) && (filterChain != null)) {filterChain.doFilter(sreq, sres); // highlight line. doFilter() calls servlet.service()}sreq.removeAttribute(Globals.JSP_FILE_ATTR);}
public StandardContext() { // org.apache.catalina.core.StardardContextsuper();pipeline.setBasic(new StandardContextValve());namingResources.setContainer(this);
}
public StandardWrapper() { // org.apache.catalina.core.StardardWrappersuper();swValve=new StandardWrapperValve();pipeline.setBasic(swValve);
}
A0)要知道Tomcat中有4种容器:Engine,Host,Context 和 Wrapper;(干货——本文一直强调这一点,理解容器的层次结构对于理解tomcat非常重要)A1)StandardContext 和 StandardWrapper 都是容器:他们都继承自 ContainerBase,只不过StandardWrapper是StandardContext的子容器,而StandardWrapper是最小的容器,即它没有子容器;A2)下面分别看StandardWrapper,StandardContext的构造函数 和 ContainerBase 的变量定义;public final class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper {public StandardWrapper() {super();swValve=new StandardWrapperValve();pipeline.setBasic(swValve);} } public class StandardContext extends ContainerBase implements Context {public StandardContext() { super();pipeline.setBasic(new StandardContextValve());namingResources.setContainer(this);} } public abstract class ContainerBase implements Container, Lifecycle, Pipeline {protected Pipeline pipeline = new StandardPipeline(this); // highlight line.protected HashMap children = new HashMap();protected int debug = 0; protected LifecycleSupport lifecycle = new LifecycleSupport(this); protected ArrayList listeners = new ArrayList(); protected Loader loader = null; protected Logger logger = null; protected Manager manager = null; protected Cluster cluster = null; protected Mapper mapper = null; protected HashMap mappers = new HashMap(); protected String mapperClass = null; protected String name = null; protected Container parent = null; protected ClassLoader parentClassLoader = null; protected Pipeline pipeline = new StandardPipeline(this); protected Realm realm = null; protected DirContext resources = null; protected static StringManager sm = StringManager.getManager(Constants.Package); protected boolean started = false; protected PropertyChangeSupport support = new PropertyChangeSupport(this); }
A3)可以看到 父容器ContainerBase定义了管道StandardPipeline,而子容器StandardContext 设置StandardContextValve为基础阀;而最小的容器StandardWrapper设置StandardWrapperValve为基础阀;A4)也即 StandardContext 和 StandardWrapper 共用同一个管道,分别设置不同的基础阀;(当然,可以分别设置非基础阀,非基础阀在基础阀被调用之前调用);
p1)第一部分: allocate()首先检查 instance是否为null,若是, 则allocate()方法调用 loadServlet()方法载入相关的servlet类,然后 整型变量countAllocated加1,返回instance的值;p2)第二部分:
p2.1)若StandardWrapper表示的servlet是一个STM servlet类,则allocate()会试图从对象池中返回一个servlet实例。变量 instancePool 是一个 java.util.Stack类型的栈,其中保存了所有的STM servlet实例:private Stack instancePool = null;
p2.2)只要STM servlet实例数不超过指定的最大值,allocate()方法会返回一个 STM servlet实例。整型变量maxInstances 保存了在栈中存储的 STM servlet实例的最大值,default value = 20;private int maxInstances = 20;
p2.3)而 nInstances 保存了当前 STM servlet实例的数量(初始为0);
3)源码如下
public Servlet allocate() throws ServletException { //org.apache.catalina.core.StandardWrapper.allocate()
// part 1 begins.if (debug >= 1) log("Allocating an instance");// If we are currently unloading this servlet, throw an exceptionif (unloading)throw new ServletException(sm.getString("standardWrapper.unloading", getName()));// If not SingleThreadedModel, return the same instance every timeif (!singleThreadModel) {// Load and initialize our instance if necessaryif (instance == null) {synchronized (this) {if (instance == null) {try {instance = loadServlet();} catch (ServletException e) {throw e;} catch (Throwable e) {throw new ServletException(sm.getString("standardWrapper.allocate"), e);}}}}if (!singleThreadModel) {if (debug >= 2)log(" Returning non-STM instance");countAllocated++;return (instance);}} // part1 ends.
// part2 starts.synchronized (instancePool) {while (countAllocated >= nInstances) {// Allocate a new instance if possible, or else waitif (nInstances < maxInstances) {try {instancePool.push(loadServlet());nInstances++;} catch (ServletException e) {throw e;} catch (Throwable e) {throw new ServletException(sm.getString("standardWrapper.allocate"), e);}} else {try {instancePool.wait();} catch (InterruptedException e) {;}}}if (debug >= 2)log(" Returning allocated STM instance");countAllocated++;return (Servlet) instancePool.pop();}}// part2 ends.
public synchronized void load() throws ServletException { // org.apache.catalina.core.StandardWrapper.load()instance = loadServlet();
}public synchronized Servlet loadServlet() throws ServletException { // org.apache.catalina.core.StandardWrapper.loadServlet()// Nothing to do if we already have an instance or an instance poolif (!singleThreadModel && (instance != null))return instance;PrintStream out = System.out;if (swallowOutput) {SystemLogHandler.startCapture();}Servlet servlet = null;try {// If this "servlet" is really a JSP file, get the right class.// HOLD YOUR NOSE - this is a kludge that avoids having to do special// case Catalina-specific code in Jasper - it also requires that the// servlet path be replaced by the <jsp-file> element content in// order to be completely effectiveString actualClass = servletClass;if ((actualClass == null) && (jspFile != null)) {Wrapper jspWrapper = (Wrapper)((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);if (jspWrapper != null)actualClass = jspWrapper.getServletClass();}// Complain if no servlet class has been specifiedif (actualClass == null) {unavailable(null);throw new ServletException(sm.getString("standardWrapper.notClass", getName()));} // Acquire an instance of the class loader to be usedLoader loader = getLoader();if (loader == null) {unavailable(null);throw new ServletException(sm.getString("standardWrapper.missingLoader", getName()));} ClassLoader classLoader = loader.getClassLoader(); // Special case class loader for a container provided servletif (isContainerProvidedServlet(actualClass)) {classLoader = this.getClass().getClassLoader();log(sm.getString("standardWrapper.containerServlet", getName()));} // Load the specified servlet class from the appropriate class loaderClass classClass = null;try {if (classLoader != null) {classClass = classLoader.loadClass(actualClass);} else {classClass = Class.forName(actualClass);}} catch (ClassNotFoundException e) {unavailable(null);throw new ServletException(sm.getString("standardWrapper.missingClass", actualClass),e);}if (classClass == null) {unavailable(null);throw new ServletException(sm.getString("standardWrapper.missingClass", actualClass));} // Instantiate and initialize an instance of the servlet class itselftry {servlet = (Servlet) classClass.newInstance();} catch (ClassCastException e) {unavailable(null);// Restore the context ClassLoaderthrow new ServletException(sm.getString("standardWrapper.notServlet", actualClass), e);} catch (Throwable e) {unavailable(null);// Restore the context ClassLoaderthrow new ServletException(sm.getString("standardWrapper.instantiate", actualClass), e);} // Check if loading the servlet in this web application should be// allowedif (!isServletAllowed(servlet)) {throw new SecurityException(sm.getString("standardWrapper.privilegedServlet",actualClass));} // Special handling for ContainerServlet instancesif ((servlet instanceof ContainerServlet) &&isContainerProvidedServlet(actualClass)) {((ContainerServlet) servlet).setWrapper(this);} // Call the initialization method of this servlettry {instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,servlet);servlet.init(facade);// Invoke jspInit on JSP pagesif ((loadOnStartup >= 0) && (jspFile != null)) {// Invoking jspInitHttpRequestBase req = new HttpRequestBase();HttpResponseBase res = new HttpResponseBase();req.setServletPath(jspFile);req.setQueryString("jsp_precompile=true");servlet.service(req, res);}instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,servlet);} catch (UnavailableException f) {instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,servlet, f);unavailable(f);throw f;} catch (ServletException f) {instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,servlet, f);// If the servlet wanted to be unavailable it would have// said so, so do not call unavailable(null).throw f;} catch (Throwable f) {instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,servlet, f);// If the servlet wanted to be unavailable it would have// said so, so do not call unavailable(null).throw new ServletException(sm.getString("standardWrapper.initException", getName()), f);} // Register our newly initialized instancesingleThreadModel = servlet instanceof SingleThreadModel;if (singleThreadModel) {if (instancePool == null)instancePool = new Stack();}fireContainerEvent("load", this);} finally {if (swallowOutput) {String log = SystemLogHandler.stopCapture();if (log != null && log.length() > 0) {if (getServletContext() != null) {getServletContext().log(log);} else {out.println(log);}}}}return servlet;}
step1)检查当前的StandardWrapper类是否表示的是一个 STM servlet类,若不是,且变量instance不为null(表示以前已经载入过这个servlet),它就直接返回该实例;// Nothing to do if we already have an instance or an instance poolif (!singleThreadModel && (instance != null))return instance;
step2)获得 System.out 和 System.err 的输出,便于它使用 javax.servlet.ServletConfig.log() 方法记录日志消息:PrintStream out = System.out;if (swallowOutput) {SystemLogHandler.startCapture();}
step3)定义类型为javax.servlet.Servlet 名为servlet 的变量,其表示已载入的servlet实例,会由 loadServlet()方法返回;Servlet servlet = null;
step4)由于Catalina是一个JSP容器,故loadServlet()方法必须检查请求的servlet是不是一个jsp 页面。若是,则loadServlet() 方法需要获取代表该jsp 页面的实际servlet类;String actualClass = servletClass;if ((actualClass == null) && (jspFile != null)) {Wrapper jspWrapper = (Wrapper)((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);if (jspWrapper != null)actualClass = jspWrapper.getServletClass(); } // public static final String JSP_SERVLET_NAME = "jsp";<span style="font-family: SimSun; line-height: 1.5; background-color: inherit;"> </span>
step5)如果找不到该jsp 页面的servlet类,则会使用变量 servletClass(actualClass)的值。若没有调用StandardWrapper.serServletClass() 方法设置servletClass的值,则会抛出异常,并停止执行后续方法;// Complain if no servlet class has been specifiedif (actualClass == null) {unavailable(null);throw new ServletException(sm.getString("standardWrapper.notClass", getName()));}<span style="font-family: SimSun; background-color: rgb(255, 255, 255);"> </span>
step6)这时,要载入的servlet类名已经解析完了,loadServlet()方法会获取载入器Loader loader = getLoader();public Loader getLoader() { // org.apache.catalina.core.ContainerBase.getLoader();if (loader != null)return (loader);if (parent != null)return (parent.getLoader());return (null);}
step7)若找到载入器(loader),则loadServlet()方法调用getClassLoader()方法获取一个ClassLoader;ClassLoader classLoader = loader.getClassLoader();
step8)Catalina提供了一些用于访问servlet容器内部数据的专用servlet类。如果某个servlet类是这种专用的servlet,即若isContainerProvidedServlet()方法返回true,则变量 classLoader被赋值为另一种ClassLoader实例,如此一来,这个servlet实例就可以访问Catalina的内部数据了;// Special case class loader for a container provided servletif (isContainerProvidedServlet(actualClass)) {classLoader = this.getClass().getClassLoader();log(sm.getString("standardWrapper.containerServlet", getName()));}
step9)准备好类载入器和准备载入的servlet类名后,loadServlet()方法就可以载入servlet类了;// Load the specified servlet class from the appropriate class loaderClass classClass = null;try {if (classLoader != null) {classClass = classLoader.loadClass(actualClass);} else {classClass = Class.forName(actualClass);}} catch (ClassNotFoundException e) {unavailable(null);throw new ServletException(sm.getString("standardWrapper.missingClass", actualClass),e);} if (classClass == null) {unavailable(null);throw new ServletException(sm.getString("standardWrapper.missingClass", actualClass));}
step10)实例化该servlet
// Instantiate and initialize an instance of the servlet class itselftry {servlet = (Servlet) classClass.newInstance();} catch (ClassCastException e) {unavailable(null);// Restore the context ClassLoaderthrow new ServletException(sm.getString("standardWrapper.notServlet", actualClass), e);} catch (Throwable e) {unavailable(null);// Restore the context ClassLoaderthrow new ServletException(sm.getString("standardWrapper.instantiate", actualClass), e);}
step11)在loadServlet()方法实例化这个servlet之前,它会调用 isServletAllowed()方法检查该servlet 类是否允许载入:
// Check if loading the servlet in this web application should be// allowedif (!isServletAllowed(servlet)) {throw new SecurityException(sm.getString("standardWrapper.privilegedServlet",actualClass));}
step12)若通过了安全检查,它还会继续检查该servlet类是否是一个 ContainerServlet类型的servlet(实现了 org.apache.catalina.ContainerServlet接口的 servlet可以访问Catalina的内部功能)。若该servlet类是一个 ContainerServlet,loadServlet()方法会调用 ContainerServlet.setWrapper(),传入StandardWrapper实例;// Special handling for ContainerServlet instancesif ((servlet instanceof ContainerServlet) &&isContainerProvidedServlet(actualClass)) {((ContainerServlet) servlet).setWrapper(this);}
step13)触发BEFORE_INIT_EVENT事件,调用servlet实例的 init()方法(init()方法传入了javax.servlet.ServletConfig外观对象):// Call the initialization method of this servlettry {instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,servlet);servlet.init(facade); // highlight line.
step14)若变量 loadOnStartup 大于0, 且被请求的servlet类实际上是一个jsp 页面,则servlet实例的service()方法;if ((loadOnStartup >= 0) && (jspFile != null)) {// Invoking jspInitHttpRequestBase req = new HttpRequestBase();HttpResponseBase res = new HttpResponseBase();req.setServletPath(jspFile);req.setQueryString("jsp_precompile=true");servlet.service(req, res); // highlight line.}instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,servlet);
step15)触发AFTER_INIT_EVENT事件instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
step16)若StandardWrapper对象表示的servlet类是一个STM servlet,则将该servlet实例添加到servlet实例池中。因此会判断 instancePool 是否为null,若是,则要给他赋值一个Stack 对象;// Register our newly initialized instancesingleThreadModel = servlet instanceof SingleThreadModel;if (singleThreadModel) {if (instancePool == null)instancePool = new Stack(); // highlight line. }
step17)在finally代码块中,loadServlet()方法停止捕获System.out 和 System.err 对象,记录在载入 ServletContext.log()方法的过程中产生的日志消息;finally {if (swallowOutput) {String log = SystemLogHandler.stopCapture();if (log != null && log.length() > 0) {if (getServletContext() != null) {getServletContext().log(log); // highlight line.} else {out.println(log);}}}} public ServletContext getServletContext() { org.apache.catalina.core.StandardWrapper.getServletContext()if (parent == null)return (null);else if (!(parent instanceof Context))return (null);elsereturn (((Context) parent).getServletContext());}
step18)最后返回已载入的servlet实例;return servlet;
public final class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper { // org.apache.catalina.core.StandardWrapper
// ......
}
public interface ServletConfig { // javax.servlet.ServletConfig public String getServletName();public ServletContext getServletContext(); public String getInitParameter(String name);public Enumeration getInitParameterNames();
}
method1)getServletConfig()方法:public ServletContext getServletContext() { // org.apache.catalina.core.StandardWrapper.getServletContext()if (parent == null)return (null);else if (!(parent instanceof Context))return (null);elsereturn (((Context) parent).getServletContext());}/*** Return the servlet context for which this Context is a facade.*/public ServletContext getServletContext() { // org.apache.catalina.core.StandardContext.getServletContext()if (context == null)context = new ApplicationContext(getBasePath(), this);return (context);}
Attention)正如以上代码所展示的那样,无法单独使用一个Wrapper实例来表示一个 servlet 类的定义。Wrapper 实例必须驻留在某个 Context 容器中,这样,当调用其父容器的getServletConfig()方法时,才能返回ServletContext类的一个实例;method2)getServletName()方法:该方法返回 servlet类的名字,该方法的签名如下:public String getServletName() { // org.apache.catalina.core.StandardWrapper.getServletName()return (getName());} public String getName() { // org.apache.catalina.core.ContainerBase.getName(). // 因为 public final class StandardWrapper extends ContainerBasereturn (name);}
method3)getInitParameter()方法:该方法返回指定初始参数的值public String getInitParameter(String name) { // org.apache.catalina.core.StandardWrapper.getInitParameter()return (findInitParameter(name));} public String findInitParameter(String name) { // org.apache.catalina.core.StandardWrapper.findInitParameter()synchronized (parameters) {return ((String) parameters.get(name));}}
对getInitParameter()方法的分析(Analysis):
A1)在StandardWrapper类中,初始化参数 parameters 存储在一个 HashMap类型中;private HashMap parameters = new HashMap();A2)通过addInitParameter()方法,传入参数的名字 和 对应的值 来填充变量 parameters 的值:
public void addInitParameter(String name, String value) { // org.apache.catalina.core.StandardWrapper.addInitParameter().synchronized (parameters) {parameters.put(name, value);}fireContainerEvent("addInitParameter", name); // highlight line.} public void fireContainerEvent(String type, Object data) {// org.apache.catalina.core.ContainerBase.fireContainerEvent().if (listeners.size() < 1)return;ContainerEvent event = new ContainerEvent(this, type, data);ContainerListener list[] = new ContainerListener[0];synchronized (listeners) {list = (ContainerListener[]) listeners.toArray(list);}for (int i = 0; i < list.length; i++)((ContainerListener) list[i]).containerEvent(event);}
A3)StandardWrapper.getInitParameter()方法的实现如下:public String getInitParameter(String name) {return (findInitParameter(name));}
A4)findInitParameter()方法接收一个指定的初始化参数名的字符串变量,调用HashMap 变量 parameters的get()方法获取初始化参数的值;public String findInitParameter(String name) { // org.apache.catalina.core.StandardWrapper.findInitParameter()synchronized (parameters) {return ((String) parameters.get(name)); // highlight line.}}
method4)getInitParameterNames()方法: 该方法返回所有初始化参数的名字的集合,实际上是 java.util.Enumeration的实例;public Enumeration getInitParameterNames() {synchronized (parameters) {return (new Enumerator(parameters.keySet()));}}
public void addChild(Container child) {throw new IllegalStateException (sm.getString("standardWrapper.notChild"));}
public void setParent(Container container) { // org.apache.catalina.core.StandardWrapper.setParent().if ((container != null) && !(container instanceof Context)) throw new IllegalArgumentException(sm.getString("standardWrapper.notContext"));if (container instanceof StandardContext) {swallowOutput = ((StandardContext)container).getSwallowOutput();}super.setParent(container); // highlight line.}
public void setParent(Container container) { // org.apache.catalina.core.ContainerBase.setParent().Container oldParent = this.parent;this.parent = container;support.firePropertyChange("parent", oldParent, this.parent);}
1.1)problem:StandardWrapper实例会调用它所载入的servlet类的实例的init()方法。init()方法需要一个javax.servlet.ServletConfig 实例,而StandardWrapper了本身也实现了 javax.servlet.ServletConfig 接口,所以,理论上 StandardWrapper需要将其中大部分公共方法对servlet程序员隐藏起来;1.2)solution:为了实现这个目的,StandardWrapper类将自身实例包装成 StandardWrapperFacade类的一个实例;
private StandardWrapperFacade facade = new StandardWrapperFacade(this); // defined in StandardWrapper.java
public StandardWrapperFacade(StandardWrapper config) {super();this.config = (ServletConfig) config;
// private ServletConfig config = null;}
public final class StandardWrapperFacade implements ServletConfig {public StandardWrapperFacade(StandardWrapper config) {super();this.config = (ServletConfig) config;}public String getServletName() {return config.getServletName();}public ServletContext getServletContext() {ServletContext theContext = config.getServletContext();if ((theContext != null) &&(theContext instanceof ApplicationContext))theContext = ((ApplicationContext) theContext).getFacade();return (theContext);}public String getInitParameter(String name) {return config.getInitParameter(name);}public Enumeration getInitParameterNames() {return config.getInitParameterNames();}
}
public StandardWrapper() { // StandardWrapper的构造函数;super();swValve=new StandardWrapperValve();pipeline.setBasic(swValve);}
O1)执行与该servlet实例关联的全部过滤器;(干货——这里引入了过滤器)O2)调用servlet实例的service()方法;
O1)调用StandardWrapper.allocate()方法获取该StandardWrapper实例所表示的 servlet实例;public void invoke(Request request, Response response,ValveContext valveContext)throws IOException, ServletException {long t1=System.currentTimeMillis();requestCount++;// Initialize local variables we may needboolean unavailable = false;Throwable throwable = null;StandardWrapper wrapper = (StandardWrapper) getContainer();ServletRequest sreq = request.getRequest();ServletResponse sres = response.getResponse();Servlet servlet = null;HttpServletRequest hreq = null;if (sreq instanceof HttpServletRequest)hreq = (HttpServletRequest) sreq;HttpServletResponse hres = null;if (sres instanceof HttpServletResponse)hres = (HttpServletResponse) sres;// Check for the application being marked unavailableif (!((Context) wrapper.getParent()).getAvailable()) {hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,sm.getString("standardContext.isUnavailable"));unavailable = true;}// Check for the servlet being marked unavailableif (!unavailable && wrapper.isUnavailable()) {log(sm.getString("standardWrapper.isUnavailable",wrapper.getName()));if (hres == null) {; // NOTE - Not much we can do generically} else {long available = wrapper.getAvailable();if ((available > 0L) && (available < Long.MAX_VALUE))hres.setDateHeader("Retry-After", available);hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,sm.getString("standardWrapper.isUnavailable",wrapper.getName()));}unavailable = true;}// Allocate a servlet instance to process this requesttry {if (!unavailable) {servlet = wrapper.allocate(); // highlight line.} // ......
O2)调用私有方法 createFilterChain(),创建过滤器链;// Create the filter chain for this request ApplicationFilterChain filterChain = createFilterChain(request, servlet); // for create FilterChain方法,本章节末尾;
private ApplicationFilterChain createFilterChain(Request request, Servlet servlet) { if (servlet == null)return (null);ApplicationFilterChain filterChain = new ApplicationFilterChain(); filterChain.setServlet(servlet);StandardWrapper wrapper = (StandardWrapper) getContainer();filterChain.setSupport(wrapper.getInstanceSupport());// Acquire the filter mappings for this ContextStandardContext context = (StandardContext) wrapper.getParent();FilterMap filterMaps[] = context.findFilterMaps();// If there are no filter mappings, we are doneif ((filterMaps == null) || (filterMaps.length == 0))return (filterChain);// Acquire the information we will need to match filter mappingsString requestPath = null;if (request instanceof HttpRequest) {HttpServletRequest hreq =(HttpServletRequest) request.getRequest();String contextPath = hreq.getContextPath();if (contextPath == null)contextPath = "";String requestURI = ((HttpRequest) request).getDecodedRequestURI();if (requestURI.length() >= contextPath.length())requestPath = requestURI.substring(contextPath.length());}String servletName = wrapper.getName();int n = 0;// Add the relevant path-mapped filters to this filter chainfor (int i = 0; i < filterMaps.length; i++) {if (!matchFiltersURL(filterMaps[i], requestPath))continue;ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMaps[i].getFilterName());if (filterConfig == null) {continue;}filterChain.addFilter(filterConfig);n++;}// Add filters that match on servlet name secondfor (int i = 0; i < filterMaps.length; i++) {if (!matchFiltersServlet(filterMaps[i], servletName))continue;ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMaps[i].getFilterName());if (filterConfig == null) {continue;}filterChain.addFilter(filterConfig);n++;}return (filterChain);}
O3)调用过滤器链的 doFilter()方法,其中包括调用servlet实例的service()方法;try {String jspFile = wrapper.getJspFile();if (jspFile != null)sreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile);elsesreq.removeAttribute(Globals.JSP_FILE_ATTR);if ((servlet != null) && (filterChain != null)) {filterChain.doFilter(sreq, sres); // hightlight line.}sreq.removeAttribute(Globals.JSP_FILE_ATTR); // ......public void doFilter(ServletRequest request, ServletResponse response) //org.apache.catlina.core.ApplicationFilterChain.doFileter()throws IOException, ServletException {if( System.getSecurityManager() != null ) {final ServletRequest req = request;final ServletResponse res = response;try {java.security.AccessController.doPrivileged(new java.security.PrivilegedExceptionAction(){public Object run() throws ServletException, IOException {internalDoFilter(req,res); // highlight line. internalDoFilter() 参见文末.return null;}});} catch( PrivilegedActionException pe) {Exception e = pe.getException();if (e instanceof ServletException)throw (ServletException) e;else if (e instanceof IOException)throw (IOException) e;else if (e instanceof RuntimeException)throw (RuntimeException) e;elsethrow new ServletException(e.getMessage(), e);}} else {internalDoFilter(request,response);}}
private void internalDoFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException { //org.apache.catalina.core.ApplicationFilterChain.internalDoFilter().// Construct an iterator the first time this method is calledif (this.iterator == null)this.iterator = filters.iterator();// Call the next filter if there is oneif (this.iterator.hasNext()) {ApplicationFilterConfig filterConfig =(ApplicationFilterConfig) iterator.next();Filter filter = null;try {filter = filterConfig.getFilter();support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,filter, request, response);filter.doFilter(request, response, this);support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,filter, request, response);} //......return;}// We fell off the end of the chain -- call the servlet instancetry {support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,servlet, request, response);if ((request instanceof HttpServletRequest) &&(response instanceof HttpServletResponse)) { servlet.service((HttpServletRequest) request, (HttpServletResponse) response); // 这不就是你梦寐以求的service()方法吗?哈哈。} else {servlet.service(request, response); // and this highlight line.}support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,servlet, request, response);} //......}
O4)释放过滤器链;try {if (filterChain != null)filterChain.release(); // highlight line.} catch (Throwable e) {log(sm.getString("standardWrapper.releaseFilters",wrapper.getName()), e);if (throwable == null) {throwable = e;exception(request, response, e);}} void release() { //org.apache.catalina.core.ApplicationFilterChain.release()this.filters.clear();this.iterator = iterator;this.servlet = null;}
O5)调用Wrapper实例的 deallocate()方法;// Deallocate the allocated servlet instancetry {if (servlet != null) {wrapper.deallocate(servlet); // highlight line.}} catch (Throwable e) {log(sm.getString("standardWrapper.deallocateException",wrapper.getName()), e);if (throwable == null) {throwable = e;exception(request, response, e);}}public void deallocate(Servlet servlet) throws ServletException { //org.apache.catalina.core.StandardWrapper.deallocate()// If not SingleThreadModel, no action is requiredif (!singleThreadModel) {countAllocated--;return;}synchronized (instancePool) {countAllocated--;instancePool.push(servlet);instancePool.notify();}}
O6)若该servlet类再也不会被使用到,调用Wrapper实例的unload()方法;// If this servlet has been marked permanently unavailable,// unload it and release this instancetry {if ((servlet != null) &&(wrapper.getAvailable() == Long.MAX_VALUE)) {wrapper.unload(); // highlight line.}} // ......long t2=System.currentTimeMillis();long time=t2-t1;processingTime+=time;if( time > maxTime ) maxTime=time;}
private ApplicationFilterChain createFilterChain(Request request,Servlet servlet) {if (servlet == null)return (null);ApplicationFilterChain filterChain =new ApplicationFilterChain();filterChain.setServlet(servlet);StandardWrapper wrapper = (StandardWrapper) getContainer();filterChain.setSupport(wrapper.getInstanceSupport());StandardContext context = (StandardContext) wrapper.getParent();FilterMap filterMaps[] = context.findFilterMaps();if ((filterMaps == null) || (filterMaps.length == 0))return (filterChain);// Acquire the information we will need to match filter mappingsString requestPath = null;if (request instanceof HttpRequest) {HttpServletRequest hreq =(HttpServletRequest) request.getRequest();String contextPath = hreq.getContextPath();if (contextPath == null)contextPath = "";String requestURI = ((HttpRequest) request).getDecodedRequestURI();if (requestURI.length() >= contextPath.length())requestPath = requestURI.substring(contextPath.length());}String servletName = wrapper.getName();int n = 0; // Add the relevant path-mapped filters to this filter chainfor (int i = 0; i < filterMaps.length; i++) {if (!matchFiltersURL(filterMaps[i], requestPath)) continue;ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMaps[i].getFilterName());if (filterConfig == null) {continue; }filterChain.addFilter(filterConfig); n++;}// Add filters that match on servlet name secondfor (int i = 0; i < filterMaps.length; i++) {if (!matchFiltersServlet(filterMaps[i], servletName)) continue;ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMaps[i].getFilterName());if (filterConfig == null) {continue;}filterChain.addFilter(filterConfig);n++;}return (filterChain); }
public synchronized void unload() throws ServletException {if (!singleThreadModel && (instance == null))return;unloading = true;if (countAllocated > 0) {int nRetries = 0;while (nRetries < 10) {if (nRetries == 0) {log("Waiting for " + countAllocated +" instance(s) to be deallocated");}try {Thread.sleep(50);} catch (InterruptedException e) {;}nRetries++;}}ClassLoader oldCtxClassLoader =Thread.currentThread().getContextClassLoader();ClassLoader classLoader = instance.getClass().getClassLoader();PrintStream out = System.out;if (swallowOutput) {SystemLogHandler.startCapture();}try {instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_DESTROY_EVENT, instance);Thread.currentThread().setContextClassLoader(classLoader);instance.destroy();instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_DESTROY_EVENT, instance);} catch (Throwable t) {instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_DESTROY_EVENT, instance, t);instance = null;instancePool = null;nInstances = 0;fireContainerEvent("unload", this);unloading = false;throw new ServletException(sm.getString("standardWrapper.destroyException", getName()),t);} finally {Thread.currentThread().setContextClassLoader(oldCtxClassLoader);if (swallowOutput) {String log = SystemLogHandler.stopCapture();if (log != null && log.length() > 0) {if (getServletContext() != null) {getServletContext().log(log);} else {out.println(log);}}}}instance = null;if (singleThreadModel && (instancePool != null)) {try {Thread.currentThread().setContextClassLoader(classLoader);while (!instancePool.isEmpty()) {((Servlet) instancePool.pop()).destroy();}} catch (Throwable t) {instancePool = null;nInstances = 0;unloading = false;fireContainerEvent("unload", this);throw new ServletException(sm.getString("standardWrapper.destroyException",getName()), t);} finally {Thread.currentThread().setContextClassLoader(oldCtxClassLoader);}instancePool = null;nInstances = 0;}singleThreadModel = false;unloading = false;fireContainerEvent("unload", this); }
public final class FilterDef { // org.apache.catalina.deploy.FilterDef private String description = null;public String getDescription() {return (this.description);}public void setDescription(String description) {this.description = description;} private String displayName = null;public String getDisplayName() {return (this.displayName);}public void setDisplayName(String displayName) {this.displayName = displayName;}private String filterClass = null;public String getFilterClass() {return (this.filterClass);}public void setFilterClass(String filterClass) {this.filterClass = filterClass;} private String filterName = null;public String getFilterName() {return (this.filterName);}public void setFilterName(String filterName) {this.filterName = filterName;} private String largeIcon = null;public String getLargeIcon() {return (this.largeIcon);}public void setLargeIcon(String largeIcon) {this.largeIcon = largeIcon;} private Map parameters = new HashMap();public Map getParameterMap() {return (this.parameters);}private String smallIcon = null;public String getSmallIcon() {return (this.smallIcon);}public void setSmallIcon(String smallIcon) {this.smallIcon = smallIcon;} public void addInitParameter(String name, String value) {parameters.put(name, value);}public String toString() {StringBuffer sb = new StringBuffer("FilterDef[");sb.append("filterName=");sb.append(this.filterName);sb.append(", filterClass=");sb.append(this.filterClass);sb.append("]");return (sb.toString());}
}
public ApplicationFilterConfig(Context context, FilterDef filterDef)throws ClassCastException, ClassNotFoundException,IllegalAccessException, InstantiationException,ServletException {super();this.context = context;setFilterDef(filterDef);}
A1)Context对象表示一个web 应用程序;A2)FilterDef对象表示一个过滤器的定义;
public String getFilterName() { // org.apache.catalina.core.ApplicationFilterConfig.getFilterName().return (filterDef.getFilterName());
}
public interface Filter { // javax.servlet.Filterpublic void init(FilterConfig filterConfig) throws ServletException; public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException; public void destroy();
}
3)ApplicationFilterChain.doFilter()方法会将 ApplicationFilterChain 类自身作为第3个参数传递给过滤器的 doFilter()方法;
public void doFilter(ServletRequest request, ServletResponse response) //org.apache.catalina.ApplicationFilterChain.doFileter().throws IOException, ServletException {if( System.getSecurityManager() != null ) {final ServletRequest req = request;final ServletResponse res = response;try {java.security.AccessController.doPrivileged(new java.security.PrivilegedExceptionAction(){public Object run() throws ServletException, IOException {internalDoFilter(req,res);return null;}});} catch( PrivilegedActionException pe) {Exception e = pe.getException();if (e instanceof ServletException)throw (ServletException) e;else if (e instanceof IOException)throw (IOException) e;else if (e instanceof RuntimeException)throw (RuntimeException) e;elsethrow new ServletException(e.getMessage(), e);}} else {internalDoFilter(request,response);}}
A1)正如你所看到的,在doFilter()方法的最后一行会调用FilterChain.doFilter()方法;A2)如果某个过滤器时过滤器链中的最后一个过滤器,则会调用被请求的 servlet类的 service()方法。如果过滤器没有调用chain.doFilter()方法,则不会调用后面的过滤器;
public final class Bootstrap {public static void main(String[] args) {//invoke: http://localhost:8080/Modern or http://localhost:8080/PrimitiveSystem.setProperty("catalina.base", System.getProperty("user.dir"));Connector connector = new HttpConnector();Wrapper wrapper1 = new StandardWrapper();wrapper1.setName("Primitive");wrapper1.setServletClass("servlet.PrimitiveServlet"); // attention for servlet class,要与你的servlet目录相对应;Wrapper wrapper2 = new StandardWrapper();wrapper2.setName("Modern");wrapper2.setServletClass("servlet.ModernServlet"); // attention for servlet class,要与你的servlet目录相对应;Context context = new StandardContext();// StandardContext's start method adds a default mappercontext.setPath("/myApp");context.setDocBase("myApp");LifecycleListener listener = new SimpleContextConfig();((Lifecycle) context).addLifecycleListener(listener);context.addChild(wrapper1);context.addChild(wrapper2);// for simplicity, we don't add a valve, but you can add// valves to context or wrapper just as you did in Chapter 6Loader loader = new WebappLoader();context.setLoader(loader);// context.addServletMapping(pattern, name);context.addServletMapping("/Primitive", "Primitive");context.addServletMapping("/Modern", "Modern");// add ContextConfig. This listener is important because it configures// StandardContext (sets configured to true), otherwise StandardContext// won't startconnector.setContainer(context);try {connector.initialize();((Lifecycle) connector).start();((Lifecycle) context).start();// make the application wait until we press a key.System.in.read();((Lifecycle) context).stop();}catch (Exception e) {e.printStackTrace();}}
}
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;lib/catalina-5.5.4.jar;lib/naming-common.
jar;lib/commons-collections.jar;lib/naming-resources.jar;lib/;lib/catalina.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot com.tomca
t.chapter11.startup.Bootstrap
HttpConnector Opening server socket on all host IP addresses
HttpConnector[8080] Starting background thread
WebappLoader[/myApp]: Deploying class repositories to work directory E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\work\_\_\myApp
StandardManager[/myApp]: Seeding random number generator class java.security.SecureRandom
StandardManager[/myApp]: Seeding of random number generator has been completed
StandardManager[/myApp]: IOException while loading persisted sessions: java.io.EOFException // // 这是从文件中加载 session对象到内存,由于没有相关文件,所以加载失败,抛出异常,但这不会影响我们访问servlet,大家不要惊慌;
java.io.EOFExceptionat java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)at java.io.ObjectInputStream.readStreamHeader(Unknown Source)at java.io.ObjectInputStream.<init>(Unknown Source)at org.apache.catalina.util.CustomObjectInputStream.<init>(CustomObjectInputStream.java:103)at org.apache.catalina.session.StandardManager.load(StandardManager.java:408)at org.apache.catalina.session.StandardManager.start(StandardManager.java:655)at org.apache.catalina.core.StandardContext.start(StandardContext.java:3570)at com.tomcat.chapter11.startup.Bootstrap.main(Bootstrap.java:55)
StandardManager[/myApp]: Exception loading sessions from persistent storage
java.io.EOFExceptionat java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)at java.io.ObjectInputStream.readStreamHeader(Unknown Source)at java.io.ObjectInputStream.<init>(Unknown Source)at org.apache.catalina.util.CustomObjectInputStream.<init>(CustomObjectInputStream.java:103)at org.apache.catalina.session.StandardManager.load(StandardManager.java:408)at org.apache.catalina.session.StandardManager.start(StandardManager.java:655)at org.apache.catalina.core.StandardContext.start(StandardContext.java:3570)at com.tomcat.chapter11.startup.Bootstrap.main(Bootstrap.java:55)
ModernServlet -- init
tomcat(11)org.apache.catalina.core.StandardWrapper源码剖析相关推荐
- tomcat(12)org.apache.catalina.core.StandardContext源码剖析
[0]README 0)本文部分文字描述转自 "how tomcat works",旨在学习 "tomcat(12)StandardContext源码剖析" 的 ...
- Apache Flink fault tolerance源码剖析(六)
上篇文章我们分析了基于检查点的用户状态的保存机制--状态终端.这篇文章我们来分析barrier(中文常译为栅栏或者屏障,为了避免引入名称争议,此处仍用英文表示).检查点的barrier是提供exact ...
- Python源码剖析[19] —— 执行引擎之一般表达式(2)
Python源码剖析 --Python执行引擎之一般表达式(2) 本文作者: Robert Chen(search.pythoner@gmail.com ) 3.2 Simple.py 前面我 ...
- 求助org.apache.catalina.core.StandardService - Stopping service [Tomcat]怎么解决大神帮帮忙
springboot项目启动,昨天还好好的,突然就启动不了了.什么配置也没改,对springboot'也不熟悉,有没有大神解决下?谢谢! . ____ _ ...
- Tomcat 报异常org.apache.catalina.core.standardwrappervalve invoke
第一次 遇到这种错误最郁闷,因为根本没有语法错误,所有都是正确的, req.getSession().setAttribute("isaudit", audit); req.get ...
- 解决:org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild
严重 [RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.core.ContainerBase.addChildInternal Contain ...
- 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.ContainerBase.addChildInternal Contain
tomcat 启动报错: 20-Aug-2019 11:23:42.807 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core. ...
- [main] org.apache.catalina.core.StandardContext.startInternal 一个或多个listeners启动失败,更多详细信息查看对应的容器日志文件
使用Tomcat9启动项目(数据库使用的是Oracle),报这个错误.看了很多其它的文章都没找到报错的问题所在. idea是可以正常启动并访问.但是打成war包部署到tomcat后,项目就启动不起来, ...
- java web: 上午 org.apache.catalina.core log 信息: 将servlet[***]标记为不可用/或者XXX资源不可用
1.首先检查web.xml文件是否正确部署: 例如: 通过index.html访问servlet.Search1文件 点击提交按钮转到servlet.Search1类进行处理 那么web.xml应为: ...
最新文章
- 负载均衡,会话保持,session同步
- 总结:DCIC算法分析赛完整方案分享!
- 嵌入式系统linux之光标隐藏解决
- 消息队列---消息模型及使用场景
- 经典蓝色主题海报设计,永恒色彩趋势
- 前端如何获取联通积分_高交会来了!中国联通带你“尝鲜”最新5G应用
- 寒冬下,掉队的金立、联想、魅族们还能赶上5G班车吗?
- 转件工程--实践者的研究方法阅读笔记1
- 软考网规论文-论企业内部网的安全策略
- 23. 实例 --- 变量
- HFSS学习笔记—19.HFSS模型导出dxf文件并绘制PCB
- 康托尔、哥德尔、图灵——永恒的金色对角线(转)
- 整理:状态机的编程思想
- Tool-windows用自带命令行,将webm的视频格式转为mp4
- 2020南京大学919经济学原理金融学学硕-上岸
- 带通滤波器是什么,它的原理是什么
- Window应急响应(七 NesMiner挖矿病毒)
- 金蝶osf接口开发_调用OSF接口取待办任务总数报错!急
- 如何测试一个纸杯-----利用引导词整理测试思路
- java SSM项目基础(day 5)[实现用户添加功能(注册)]
热门文章
- Codeforces Round #712 (Div. 2) E. Travelling Salesman Problem 思维转换
- Codeforces Round #606 (Div. 2, based on Technocup 2020 Elimination Round 4) 构造
- CF1361C. Johnny and Megan‘s Necklace(构造,欧拉回路,传递闭包)
- 牛客题霸 车站建造问题 C++题解/答案
- 后缀自动机(SAM)构造实现过程演示+习题集锦
- CF1472(div3):总结
- P1912-[NOI2009]诗人小G【四边形不等式,单调队列】
- P4351-[CERC2015]Frightful Formula【组合数学,MTT】
- P3233-[HNOI2014]世界树【虚树,倍增】
- P6772-[NOI2020]美食家【矩阵乘法,倍增】