现在的位置: 首页 > 综合 > 正文

Tomcat7中web应用加载原理(三)Listener、Filter、Servlet的加载和调用

2017年11月20日 ⁄ 综合 ⁄ 共 38497字 ⁄ 字号 评论关闭

前一篇文章分析到了org.apache.catalina.deploy.WebXml类的configureContext方法,可以看到在这个方法中通过各种setXXX、addXXX方法的调用,使得每个应用中的web.xml文件的解析后将应用内部的表示Servlet、Listener、Filter的配置信息与表示一个web应用的Context对象关联起来。

这里列出configureContext方法中与Servlet、Listener、Filter的配置信息设置相关的调用代码:

Java代码  收藏代码
  1. for (FilterDef filter : filters.values()) {  
  2.     if (filter.getAsyncSupported() == null) {  
  3.         filter.setAsyncSupported("false");  
  4.     }  
  5.     context.addFilterDef(filter);  
  6. }  
  7. for (FilterMap filterMap : filterMaps) {  
  8.     context.addFilterMap(filterMap);  
  9. }  

这是设置Filter相关配置信息的。

 

Java代码  收藏代码
  1. for (String listener : listeners) {  
  2.     context.addApplicationListener(  
  3.             new ApplicationListener(listener, false));  
  4. }  

这是给应用添加Listener的。

 

Java代码  收藏代码
  1. for (ServletDef servlet : servlets.values()) {  
  2.     Wrapper wrapper = context.createWrapper();  
  3.     // Description is ignored  
  4.     // Display name is ignored  
  5.     // Icons are ignored  
  6.   
  7.     // jsp-file gets passed to the JSP Servlet as an init-param  
  8.   
  9.     if (servlet.getLoadOnStartup() != null) {  
  10.         wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());  
  11.     }  
  12.     if (servlet.getEnabled() != null) {  
  13.         wrapper.setEnabled(servlet.getEnabled().booleanValue());  
  14.     }  
  15.     wrapper.setName(servlet.getServletName());  
  16.     Map<String,String> params = servlet.getParameterMap();  
  17.     for (Entry<String, String> entry : params.entrySet()) {  
  18.         wrapper.addInitParameter(entry.getKey(), entry.getValue());  
  19.     }  
  20.     wrapper.setRunAs(servlet.getRunAs());  
  21.     Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();  
  22.     for (SecurityRoleRef roleRef : roleRefs) {  
  23.         wrapper.addSecurityReference(  
  24.                 roleRef.getName(), roleRef.getLink());  
  25.     }  
  26.     wrapper.setServletClass(servlet.getServletClass());  
  27.     MultipartDef multipartdef = servlet.getMultipartDef();  
  28.     if (multipartdef != null) {  
  29.         if (multipartdef.getMaxFileSize() != null &&  
  30.                 multipartdef.getMaxRequestSize()!= null &&  
  31.                 multipartdef.getFileSizeThreshold() != null) {  
  32.             wrapper.setMultipartConfigElement(new MultipartConfigElement(  
  33.                     multipartdef.getLocation(),  
  34.                     Long.parseLong(multipartdef.getMaxFileSize()),  
  35.                     Long.parseLong(multipartdef.getMaxRequestSize()),  
  36.                     Integer.parseInt(  
  37.                             multipartdef.getFileSizeThreshold())));  
  38.         } else {  
  39.             wrapper.setMultipartConfigElement(new MultipartConfigElement(  
  40.                     multipartdef.getLocation()));  
  41.         }  
  42.     }  
  43.     if (servlet.getAsyncSupported() != null) {  
  44.         wrapper.setAsyncSupported(  
  45.                 servlet.getAsyncSupported().booleanValue());  
  46.     }  
  47.     wrapper.setOverridable(servlet.isOverridable());  
  48.     context.addChild(wrapper);  
  49. }  
  50. for (Entry<String, String> entry : servletMappings.entrySet()) {  
  51.     context.addServletMapping(entry.getKey(), entry.getValue());  
  52. }  

这段代码是设置Servlet的相关配置信息的。

 

以上是在各个web应用的web.xml文件中(如果是servlet3,还会包括将这些配置信息放在类的注解中,所以解析web.xml文件之前可能会存在各个web.xml文件信息的合并步骤,这些动作的代码在前一篇文章中讲ContextConfig类的webConfig方法中)的相关配置信息的设置,但需要注意的是,这里仅仅是将这些配置信息保存到了StandardContext的相应实例变量中,真正在一次请求访问中用到的Servlet、Listener、Filter的实例并没有构造出来,以上方法调用仅构造了代表这些实例的封装类的实例,如StandardWrapper、ApplicationListener、FilterDef、FilterMap。

 

那么一个web应用中的Servlet、Listener、Filter的实例究竟在什么时候构造出来的呢?答案在org.apache.catalina.core.StandardContext类的startInternal方法中:

Java代码  收藏代码
  1. protected synchronized void startInternal() throws LifecycleException {  
  2.   
  3.     if(log.isDebugEnabled())  
  4.         log.debug("Starting " + getBaseName());  
  5.   
  6.     // Send j2ee.state.starting notification   
  7.     if (this.getObjectName() != null) {  
  8.         Notification notification = new Notification("j2ee.state.starting",  
  9.                 this.getObjectName(), sequenceNumber.getAndIncrement());  
  10.         broadcaster.sendNotification(notification);  
  11.     }  
  12.   
  13.     setConfigured(false);  
  14.     boolean ok = true;  
  15.   
  16.     // Currently this is effectively a NO-OP but needs to be called to  
  17.     // ensure the NamingResources follows the correct lifecycle  
  18.     if (namingResources != null) {  
  19.         namingResources.start();  
  20.     }  
  21.       
  22.     // Add missing components as necessary  
  23.     if (webappResources == null) {   // (1) Required by Loader  
  24.         if (log.isDebugEnabled())  
  25.             log.debug("Configuring default Resources");  
  26.         try {  
  27.             if ((getDocBase() != null) && (getDocBase().endsWith(".war")) &&  
  28.                     (!(new File(getBasePath())).isDirectory()))  
  29.                 setResources(new WARDirContext());  
  30.             else  
  31.                 setResources(new FileDirContext());  
  32.         } catch (IllegalArgumentException e) {  
  33.             log.error("Error initializing resources: " + e.getMessage());  
  34.             ok = false;  
  35.         }  
  36.     }  
  37.     if (ok) {  
  38.         if (!resourcesStart()) {  
  39.             log.error( "Error in resourceStart()");  
  40.             ok = false;  
  41.         }  
  42.     }  
  43.   
  44.     if (getLoader() == null) {  
  45.         WebappLoader webappLoader = new WebappLoader(getParentClassLoader());  
  46.         webappLoader.setDelegate(getDelegate());  
  47.         setLoader(webappLoader);  
  48.     }  
  49.   
  50.     // Initialize character set mapper  
  51.     getCharsetMapper();  
  52.   
  53.     // Post work directory  
  54.     postWorkDirectory();  
  55.   
  56.     // Validate required extensions  
  57.     boolean dependencyCheck = true;  
  58.     try {  
  59.         dependencyCheck = ExtensionValidator.validateApplication  
  60.             (getResources(), this);  
  61.     } catch (IOException ioe) {  
  62.         log.error("Error in dependencyCheck", ioe);  
  63.         dependencyCheck = false;  
  64.     }  
  65.   
  66.     if (!dependencyCheck) {  
  67.         // do not make application available if depency check fails  
  68.         ok = false;  
  69.     }  
  70.   
  71.     // Reading the "catalina.useNaming" environment variable  
  72.     String useNamingProperty = System.getProperty("catalina.useNaming");  
  73.     if ((useNamingProperty != null)  
  74.         && (useNamingProperty.equals("false"))) {  
  75.         useNaming = false;  
  76.     }  
  77.   
  78.     if (ok && isUseNaming()) {  
  79.         if (getNamingContextListener() == null) {  
  80.             NamingContextListener ncl = new NamingContextListener();  
  81.             ncl.setName(getNamingContextName());  
  82.             ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());  
  83.             addLifecycleListener(ncl);  
  84.             setNamingContextListener(ncl);  
  85.         }  
  86.     }  
  87.       
  88.     // Standard container startup  
  89.     if (log.isDebugEnabled())  
  90.         log.debug("Processing standard container startup");  
  91.   
  92.       
  93.     // Binding thread  
  94.     ClassLoader oldCCL = bindThread();  
  95.   
  96.     try {  
  97.   
  98.         if (ok) {  
  99.               
  100.             // Start our subordinate components, if any  
  101.             if ((loader != null) && (loader instanceof Lifecycle))  
  102.                 ((Lifecycle) loader).start();  
  103.   
  104.             // since the loader just started, the webapp classloader is now  
  105.             // created.  
  106.             // By calling unbindThread and bindThread in a row, we setup the  
  107.             // current Thread CCL to be the webapp classloader  
  108.             unbindThread(oldCCL);  
  109.             oldCCL = bindThread();  
  110.   
  111.             // Initialize logger again. Other components might have used it  
  112.             // too early, so it should be reset.  
  113.             logger = null;  
  114.             getLogger();  
  115.               
  116.             if ((cluster != null) && (cluster instanceof Lifecycle))  
  117.                 ((Lifecycle) cluster).start();  
  118.             Realm realm = getRealmInternal();  
  119.             if ((realm != null) && (realm instanceof Lifecycle))  
  120.                 ((Lifecycle) realm).start();  
  121.             if ((resources != null) && (resources instanceof Lifecycle))  
  122.                 ((Lifecycle) resources).start();  
  123.   
  124.             // Notify our interested LifecycleListeners  
  125.             fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);  
  126.               
  127.             // Start our child containers, if not already started  
  128.             for (Container child : findChildren()) {  
  129.                 if (!child.getState().isAvailable()) {  
  130.                     child.start();  
  131.                 }  
  132.             }  
  133.   
  134.             // Start the Valves in our pipeline (including the basic),  
  135.             // if any  
  136.             if (pipeline instanceof Lifecycle) {  
  137.                 ((Lifecycle) pipeline).start();  
  138.             }  
  139.               
  140.             // Acquire clustered manager  
  141.             Manager contextManager = null;  
  142.             if (manager == null) {  
  143.                 if (log.isDebugEnabled()) {  
  144.                     log.debug(sm.getString("standardContext.cluster.noManager",  
  145.                             Boolean.valueOf((getCluster() != null)),  
  146.                             Boolean.valueOf(distributable)));  
  147.                 }  
  148.                 if ( (getCluster() != null) && distributable) {  
  149.                     try {  
  150.                         contextManager = getCluster().createManager(getName());  
  151.                     } catch (Exception ex) {  
  152.                         log.error("standardContext.clusterFail", ex);  
  153.                         ok = false;  
  154.                     }  
  155.                 } else {  
  156.                     contextManager = new StandardManager();  
  157.                 }  
  158.             }   
  159.               
  160.             // Configure default manager if none was specified  
  161.             if (contextManager != null) {  
  162.                 if (log.isDebugEnabled()) {  
  163.                     log.debug(sm.getString("standardContext.manager",  
  164.                             contextManager.getClass().getName()));  
  165.                 }  
  166.                 setManager(contextManager);  
  167.             }  
  168.   
  169.             if (manager!=null && (getCluster() != null) && distributable) {  
  170.                 //let the cluster know that there is a context that is distributable  
  171.                 //and that it has its own manager  
  172.                 getCluster().registerManager(manager);  
  173.             }  
  174.         }  
  175.   
  176.     } finally {  
  177.         // Unbinding thread  
  178.         unbindThread(oldCCL);  
  179.     }  
  180.   
  181.     if (!getConfigured()) {  
  182.         log.error( "Error getConfigured");  
  183.         ok = false;  
  184.     }  
  185.   
  186.     // We put the resources into the servlet context  
  187.     if (ok)  
  188.         getServletContext().setAttribute  
  189.             (Globals.RESOURCES_ATTR, getResources());  
  190.   
  191.     // Initialize associated mapper  
  192.     mapper.setContext(getPath(), welcomeFiles, resources);  
  193.   
  194.     // Binding thread  
  195.     oldCCL = bindThread();  
  196.   
  197.     if (ok ) {  
  198.         if (getInstanceManager() == null) {  
  199.             javax.naming.Context context = null;  
  200.             if (isUseNaming() && getNamingContextListener() != null) {  
  201.                 context = getNamingContextListener().getEnvContext();  
  202.             }  
  203.             Map<String, Map<String, String>> injectionMap = buildInjectionMap(  
  204.                     getIgnoreAnnotations() ? new NamingResources(): getNamingResources());  
  205.             setInstanceManager(new DefaultInstanceManager(context,  
  206.                     injectionMap, thisthis.getClass().getClassLoader()));  
  207.             getServletContext().setAttribute(  
  208.                     InstanceManager.class.getName(), getInstanceManager());  
  209.         }  
  210.     }  
  211.   
  212.     try {  
  213.         // Create context attributes that will be required  
  214.         if (ok) {  
  215.             getServletContext().setAttribute(  
  216.                     JarScanner.class.getName(), getJarScanner());  
  217.         }  
  218.   
  219.         // Set up the context init params  
  220.         mergeParameters();  
  221.   
  222.         // Call ServletContainerInitializers  
  223.         for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :  
  224.             initializers.entrySet()) {  
  225.             try {  
  226.                 entry.getKey().onStartup(entry.getValue(),  
  227.                         getServletContext());  
  228.             } catch (ServletException e) {  
  229.                 log.error(sm.getString("standardContext.sciFail"), e);  
  230.                 ok = false;  
  231.                 break;  
  232.             }  
  233.         }  
  234.   
  235.         // Configure and call application event listeners  
  236.         if (ok) {  
  237.             if (!listenerStart()) {  
  238.                 log.error( "Error listenerStart");  
  239.                 ok = false;  
  240.             }  
  241.         }  
  242.           
  243.         try {  
  244.             // Start manager  
  245.             if ((manager != null) && (manager instanceof Lifecycle)) {  
  246.                 ((Lifecycle) getManager()).start();  
  247.             }  
  248.         } catch(Exception e) {  
  249.             log.error("Error manager.start()", e);  
  250.             ok = false;  
  251.         }  
  252.   
  253.         // Configure and call application filters  
  254.         if (ok) {  
  255.             if (!filterStart()) {  
  256.                 log.error("Error filterStart");  
  257.                 ok = false;  
  258.             }  
  259.         }  
  260.           
  261.         // Load and initialize all "load on startup" servlets  
  262.         if (ok) {  
  263.             loadOnStartup(findChildren());  
  264.         }  
  265.           
  266.         // Start ContainerBackgroundProcessor thread  
  267.         super.threadStart();  
  268.     } finally {  
  269.         // Unbinding thread  
  270.         unbindThread(oldCCL);  
  271.     }  
  272.   
  273.     // Set available status depending upon startup success  
  274.     if (ok) {  
  275.         if (log.isDebugEnabled())  
  276.             log.debug("Starting completed");  
  277.     } else {  
  278.         log.error(sm.getString("standardContext.startFailed", getName()));  
  279.     }  
  280.   
  281.     startTime=System.currentTimeMillis();  
  282.       
  283.     // Send j2ee.state.running notification   
  284.     if (ok && (this.getObjectName() != null)) {  
  285.         Notification notification =   
  286.             new Notification("j2ee.state.running"this.getObjectName(),  
  287.                              sequenceNumber.getAndIncrement());  
  288.         broadcaster.sendNotification(notification);  
  289.     }  
  290.   
  291.     // Close all JARs right away to avoid always opening a peak number   
  292.     // of files on startup  
  293.     if (getLoader() instanceof WebappLoader) {  
  294.         ((WebappLoader) getLoader()).closeJARs(true);  
  295.     }  
  296.   
  297.     // Reinitializing if something went wrong  
  298.     if (!ok) {  
  299.         setState(LifecycleState.FAILED);  
  300.     } else {  
  301.         setState(LifecycleState.STARTING);  
  302.     }  
  303. }  

这303行可以讲的东西有很多,为了不偏离本文主题只抽出与现在要讨论的问题相关的代码来分析。

 

第125行会发布一个CONFIGURE_START_EVENT事件,按前一篇博文所述,这里即会触发对web.xml的解析。第205、206行设置实例管理器为DefaultInstanceManager(这个类在后面谈实例构造时会用到)。第237行会调用listenerStart方法,第255行调用了filterStart方法,第263行调用了loadOnStartup方法,这三处调用即触发Listener、Filter、Servlet真正对象的构造,下面逐个分析这些方法。

 

listenerStart方法的完整代码较长,这里仅列出与Listenner对象构造相关的代码:

Java代码  收藏代码
  1. // Instantiate the required listeners  
  2. ApplicationListener listeners[] = applicationListeners;  
  3. Object results[] = new Object[listeners.length];  
  4. boolean ok = true;  
  5. Set<Object> noPluggabilityListeners = new HashSet<Object>();  
  6. for (int i = 0; i < results.length; i++) {  
  7.     if (getLogger().isDebugEnabled())  
  8.         getLogger().debug(" Configuring event listener class '" +  
  9.             listeners[i] + "'");  
  10.     try {  
  11.         ApplicationListener listener = listeners[i];  
  12.         results[i] = instanceManager.newInstance(  
  13.                 listener.getClassName());  
  14.         if (listener.isPluggabilityBlocked()) {  
  15.             noPluggabilityListeners.add(results[i]);  
  16.         }  
  17.     } catch (Throwable t) {  
  18.         t = ExceptionUtils.unwrapInvocationTargetException(t);  
  19.         ExceptionUtils.handleThrowable(t);  
  20.         getLogger().error  
  21.             (sm.getString("standardContext.applicationListener",  
  22.                           listeners[i]), t);  
  23.         ok = false;  
  24.     }  
  25. }  

先从Context对象中取出实例变量applicationListeners(该变量的值在web.xml解析时设置),第12行通过调用instanceManager.newInstance(listener.getClassName()),前面在看StandardContext的startInternal方法第205行时看到instanceManager被设置为DefaultInstanceManager对象,所以这里实际会执行DefaultInstanceManager类的newInstance方法:

Java代码  收藏代码
  1. public Object newInstance(String className) throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException, ClassNotFoundException {  
  2.     Class<?> clazz = loadClassMaybePrivileged(className, classLoader);  
  3.     return newInstance(clazz.newInstance(), clazz);  
  4. }  

所以instanceManager.newInstance(listener.getClassName())这段代码的作用是取出web.xml中配置的Listener的class配置信息,从而构造实际配置的Listener对象。

 

看下filterStart方法:

Java代码  收藏代码
  1. public boolean filterStart() {  
  2.   
  3.     if (getLogger().isDebugEnabled())  
  4.         getLogger().debug("Starting filters");  
  5.     // Instantiate and record a FilterConfig for each defined filter  
  6.     boolean ok = true;  
  7.     synchronized (filterConfigs) {  
  8.         filterConfigs.clear();  
  9.         Iterator<String> names = filterDefs.keySet().iterator();  
  10.         while (names.hasNext()) {  
  11.             String name = names.next();  
  12.             if (getLogger().isDebugEnabled())  
  13.                 getLogger().debug(" Starting filter '" + name + "'");  
  14.             ApplicationFilterConfig filterConfig = null;  
  15.             try {  
  16.                 filterConfig =  
  17.                     new ApplicationFilterConfig(this, filterDefs.get(name));  
  18.                 filterConfigs.put(name, filterConfig);  
  19.             } catch (Throwable t) {  
  20.                 t = ExceptionUtils.unwrapInvocationTargetException(t);  
  21.                 ExceptionUtils.handleThrowable(t);  
  22.                 getLogger().error  
  23.                     (sm.getString("standardContext.filterStart", name), t);  
  24.                 ok = false;  
  25.             }  
  26.         }  
  27.     }  
  28.   
  29.     return (ok);  
  30.   
  31. }  

这段代码看起来很简单,取出web.xml解析时读到的filter配置信息,在第17行调用ApplicationFilterConfig了构造方法:

Java代码  收藏代码
  1. ApplicationFilterConfig(Context context, FilterDef filterDef)  
  2.     throws ClassCastException, ClassNotFoundException,  
  3.            IllegalAccessException, InstantiationException,  
  4.            ServletException, InvocationTargetException, NamingException {  
  5.   
  6.     super();  
  7.   
  8.     this.context = context;  
  9.     this.filterDef = filterDef;  
  10.     // Allocate a new filter instance if necessary  
  11.     if (filterDef.getFilter() == null) {  
  12.         getFilter();  
  13.     } else {  
  14.         this.filter = filterDef.getFilter();  
  15.         getInstanceManager().newInstance(filter);  
  16.         initFilter();  
  17.     }  
  18. }  

默认情况下filterDef中是没有Filter对象的,所以会调用第12行getFilter方法:

Java代码  收藏代码
  1. Filter getFilter() throws ClassCastException, ClassNotFoundException,  
  2.     IllegalAccessException, InstantiationException, ServletException,  
  3.     InvocationTargetException, NamingException {  
  4.   
  5.     // Return the existing filter instance, if any  
  6.     if (this.filter != null)  
  7.         return (this.filter);  
  8.   
  9.     // Identify the class loader we will be using  
  10.     String filterClass = filterDef.getFilterClass();  
  11.     this.filter = (Filter) getInstanceManager().newInstance(filterClass);  
  12.       
  13.     initFilter();  
  14.       
  15.     return (this.filter);  
  16.   
  17. }  

与Listener的对象构造类似,都是通过调用getInstanceManager().newInstance方法。当然,按照Servlet规范,第13行还会调用Filter的init方法。

 

看下loadOnStartup方法: 

Java代码  收藏代码
  1. public void loadOnStartup(Container children[]) {  
  2.   
  3.     // Collect "load on startup" servlets that need to be initialized  
  4.     TreeMap<Integer, ArrayList<Wrapper>> map =  
  5.         new TreeMap<Integer, ArrayList<Wrapper>>();  
  6.     for (int i = 0; i < children.length; i++) {  
  7.         Wrapper wrapper = (Wrapper) children[i];  
  8.         int loadOnStartup = wrapper.getLoadOnStartup();  
  9.         if (loadOnStartup < 0)  
  10.             continue;  
  11.         Integer key = Integer.valueOf(loadOnStartup);  
  12.         ArrayList<Wrapper> list = map.get(key);  
  13.         if (list == null) {  
  14.             list = new ArrayList<Wrapper>();  
  15.             map.put(key, list);  
  16.         }  
  17.         list.add(wrapper);  
  18.     }  
  19.   
  20.     // Load the collected "load on startup" servlets  
  21.     for (ArrayList<Wrapper> list : map.values()) {  
  22.         for (Wrapper wrapper : list) {  
  23.             try {  
  24.                 wrapper.load();  
  25.             } catch (ServletException e) {  
  26.                 getLogger().error(sm.getString("standardWrapper.loadException",  
  27.                                   getName()), StandardWrapper.getRootCause(e));  
  28.                 // NOTE: load errors (including a servlet that throws  
  29.                 // UnavailableException from tht init() method) are NOT  
  30.                 // fatal to application startup  
  31.             }  
  32.         }  
  33.     }  
  34.   
  35. }  

在web应用启动时将会加载配置了load-on-startup属性的Servlet。第24行,调用了StandardWrapper类的load方法:

Java代码  收藏代码
  1. public synchronized void load() throws ServletException {  
  2.     instance = loadServlet();  
  3.       
  4.     if (!instanceInitialized) {  
  5.         initServlet(instance);  
  6.     }  
  7.   
  8.     if (isJspServlet) {  
  9.         StringBuilder oname =  
  10.             new StringBuilder(MBeanUtils.getDomain(getParent()));  
  11.           
  12.         oname.append(":type=JspMonitor,name=");  
  13.         oname.append(getName());  
  14.           
  15.         oname.append(getWebModuleKeyProperties());  
  16.           
  17.         try {  
  18.             jspMonitorON = new ObjectName(oname.toString());  
  19.             Registry.getRegistry(nullnull)  
  20.                 .registerComponent(instance, jspMonitorON, null);  
  21.         } catch( Exception ex ) {  
  22.             log.info("Error registering JSP monitoring with jmx " +  
  23.                      instance);  
  24.         }  
  25.     }  
  26. }  

在第2行loadServlet方法中与上面的Listener和Filter对象构造一样调用instanceManager.newInstance来构造Servlet对象,与Filter类似在第5行调用Servlet的init方法。

当然,这种加载只是针对配置了load-on-startup属性的Servlet而言,其它一般Servlet的加载和初始化会推迟到真正请求访问web应用而第一次调用该Servlet时,下面会看到这种情况下代码分析。

 

以上分析的web应用启动后这些对象的加载情况,接下来分析一下一次请求访问时,相关的Filter、Servlet对象的调用。

 

在本博前面的《Tomcat7中一次请求处理的前世今生》系列文章中曾经分析了一次请求如何与容器中的Engine、Host、Context、Wrapper各级组件匹配,并在这些容器组件内部的管道中流转的。在该系列第四篇文章最后提到,一次请求最终会执行与它最适配的一个StandardWrapper的基础阀org.apache.catalina.core.StandardWrapperValve的invoke方法。当时限于篇幅没继续往下分析,这里接着这段来看看请求的流转。看下invoke方法的代码:

Java代码  收藏代码
  1. public final void invoke(Request request, Response response)  
  2.     throws IOException, ServletException {  
  3.   
  4.     // Initialize local variables we may need  
  5.     boolean unavailable = false;  
  6.     Throwable throwable = null;  
  7.     // This should be a Request attribute...  
  8.     long t1=System.currentTimeMillis();  
  9.     requestCount++;  
  10.     StandardWrapper wrapper = (StandardWrapper) getContainer();  
  11.     Servlet servlet = null;  
  12.     Context context = (Context) wrapper.getParent();  
  13.       
  14.     // Check for the application being marked unavailable  
  15.     if (!context.getState().isAvailable()) {  
  16.         response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,  
  17.                        sm.getString("standardContext.isUnavailable"));  
  18.         unavailable = true;  
  19.     }  
  20.   
  21.     // Check for the servlet being marked unavailable  
  22.     if (!unavailable && wrapper.isUnavailable()) {  
  23.         container.getLogger().info(sm.getString("standardWrapper.isUnavailable",  
  24.                 wrapper.getName()));  
  25.         long available = wrapper.getAvailable();  
  26.         if ((available > 0L) && (available < Long.MAX_VALUE)) {  
  27.             response.setDateHeader("Retry-After", available);  
  28.             response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,  
  29.                     sm.getString("standardWrapper.isUnavailable",  
  30.                             wrapper.getName()));  
  31.         } else if (available == Long.MAX_VALUE) {  
  32.             response.sendError(HttpServletResponse.SC_NOT_FOUND,  
  33.                     sm.getString("standardWrapper.notFound",  
  34.                             wrapper.getName()));  
  35.         }  
  36.         unavailable = true;  
  37.     }  
  38.   
  39.     // Allocate a servlet instance to process this request  
  40.     try {  
  41.         if (!unavailable) {  
  42.             servlet = wrapper.allocate();  
  43.         }  
  44.     } catch (UnavailableException e) {  
  45.         container.getLogger().error(  
  46.                 sm.getString("standardWrapper.allocateException",  
  47.                         wrapper.getName()), e);  
  48.         long available = wrapper.getAvailable();  
  49.         if ((available > 0L) && (available < Long.MAX_VALUE)) {  
  50.             response.setDateHeader("Retry-After", available);  
  51.             response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,  
  52.                        sm.getString("standardWrapper.isUnavailable",  
  53.                                     wrapper.getName()));  
  54.         } else if (available == Long.MAX_VALUE) {  
  55.             response.sendError(HttpServletResponse.SC_NOT_FOUND,  
  56.                        sm.getString("standardWrapper.notFound",  
  57.                                     wrapper.getName()));  
  58.         }  
  59.     } catch (ServletException e) {  
  60.         container.getLogger().error(sm.getString("standardWrapper.allocateException",  
  61.                          wrapper.getName()), StandardWrapper.getRootCause(e));  
  62.         throwable = e;  
  63.         exception(request, response, e);  
  64.     } catch (Throwable e) {  
  65.         ExceptionUtils.handleThrowable(e);  
  66.         container.getLogger().error(sm.getString("standardWrapper.allocateException",  
  67.                          wrapper.getName()), e);  
  68.         throwable = e;  
  69.         exception(request, response, e);  
  70.         servlet = null;  
  71.     }  
  72.   
  73.     // Identify if the request is Comet related now that the servlet has been allocated  
  74.     boolean comet = false;  
  75.     if (servlet instanceof CometProcessor && request.getAttribute(  
  76.             Globals.COMET_SUPPORTED_ATTR) == Boolean.TRUE) {  
  77.         comet = true;  
  78.         request.setComet(true);  
  79.     }  
  80.       
  81.     MessageBytes requestPathMB = request.getRequestPathMB();  
  82.     DispatcherType dispatcherType = DispatcherType.REQUEST;  
  83.     if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;   
  84.     request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);  
  85.     request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,  
  86.             requestPathMB);  
  87.     // Create the filter chain for this request  
  88.     ApplicationFilterFactory factory =  
  89.         ApplicationFilterFactory.getInstance();  
  90.     ApplicationFilterChain filterChain =  
  91.         factory.createFilterChain(request, wrapper, servlet);  
  92.       
  93.     // Reset comet flag value after creating the filter chain  
  94.     request.setComet(false);  
  95.   
  96.     // Call the filter chain for this request  
  97.     // NOTE: This also calls the servlet's service() method  
  98.     try {  
  99.         if ((servlet != null) && (filterChain != null)) {  
  100.             // Swallow output if needed  
  101.             if (context.getSwallowOutput()) {  
  102.                 try {  
  103.                     SystemLogHandler.startCapture();  
  104.                     if (request.isAsyncDispatching()) {  
  105.                         //TODO SERVLET3 - async  
  106.                         ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();   
  107.                     } else if (comet) {  
  108.                         filterChain.doFilterEvent(request.getEvent());  
  109.                         request.setComet(true);  
  110.                     } else {  
  111.                         filterChain.doFilter(request.getRequest(),   
  112.                                 response.getResponse());  
  113.                     }  
  114.                 } finally {  
  115.                     String log = SystemLogHandler.stopCapture();  
  116.                     if (log != null && log.length() > 0) {  
  117.                         context.getLogger().info(log);  
  118.                     }  
  119.                 }  
  120.             } else {  
  121.                 if (request.isAsyncDispatching()) {  
  122.                     //TODO SERVLET3 - async  
  123.                     ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();  
  124.                 } else if (comet) {  
  125.                     request.setComet(true);  
  126.                     filterChain.doFilterEvent(request.getEvent());  
  127.                 } else {  
  128.                     filterChain.doFilter  
  129.                         (request.getRequest(), response.getResponse());  
  130.                 }  
  131.             }  
  132.   
  133.         }  
  134.     } catch (ClientAbortException e) {  
  135.         throwable = e;  
  136.         exception(request, response, e);  
  137.     } catch (IOException e) {  
  138.         container.getLogger().error(sm.getString(  
  139.                 "standardWrapper.serviceException", wrapper.getName(),  
  140.                 context.getName()), e);  
  141.         throwable = e;  
  142.         exception(request, response, e);  
  143.     } catch (UnavailableException e) {  
  144.         container.getLogger().error(sm.getString(  
  145.                 "standardWrapper.serviceException", wrapper.getName(),  
  146.                 context.getName()), e);  
  147.         //            throwable = e;  
  148.         //            exception(request, response, e);  
  149.         wrapper.unavailable(e);  
  150.         long available = wrapper.getAvailable();  
  151.         if ((available > 0L) && (available < Long.MAX_VALUE)) {  
  152.             response.setDateHeader("Retry-After", available);  
  153.             response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,  
  154.                        sm.getString("standardWrapper.isUnavailable",  
  155.                                     wrapper.getName()));  
  156.         } else if (available == Long.MAX_VALUE) {  
  157.             response.sendError(HttpServletResponse.SC_NOT_FOUND,  
  158.                         sm.getString("standardWrapper.notFound",  
  159.                                     wrapper.getName()));  
  160.         }  
  161.         // Do not save exception in 'throwable', because we  
  162.         // do not want to do exception(request, response, e) processing  
  163.     } catch (ServletException e) {  
  164.         Throwable rootCause = StandardWrapper.getRootCause(e);  
  165.         if (!(rootCause instanceof ClientAbortException)) {  
  166.             container.getLogger().error(sm.getString(  
  167.                     "standardWrapper.serviceExceptionRoot",  
  168.                     wrapper.getName(), context.getName(), e.getMessage()),  
  169.                     rootCause);  
  170.         }  
  171.         throwable = e;  
  172.         exception(request, response, e);  
  173.     } catch (Throwable e) {  
  174.         ExceptionUtils.handleThrowable(e);  
  175.         container.getLogger().error(sm.getString(  
  176.                 "standardWrapper.serviceException", wrapper.getName(),  
  177.                 context.getName()), e);  
  178.         throwable = e;  
  179.         exception(request, response, e);  
  180.     }  
  181.   
  182.     // Release the filter chain (if any) for this request  
  183.     if (filterChain != null) {  
  184.         if (request.isComet()) {  
  185.             // If this is a Comet request, then the same chain will be used for the  
  186.             // processing of all subsequent events.  
  187.             filterChain.reuse();  
  188.         } else {  
  189.             filterChain.release();  
  190.         }  
  191.     }  
  192.   
  193.     // Deallocate the allocated servlet instance  
  194.     try {  
  195.         if (servlet != null) {  
  196.             wrapper.deallocate(servlet);  
  197.         }  
  198.     } catch (Throwable e) {  
  199.         ExceptionUtils.handleThrowable(e);  
  200.         container.getLogger().error(sm.getString("standardWrapper.deallocateException",  
  201.                          wrapper.getName()), e);  
  202.         if (throwable == null) {  
  203.             throwable = e;  
  204.             exception(request, response, e);  
  205.         }  
  206.     }  
  207.   
  208.     // If this servlet has been marked permanently unavailable,  
  209.     // unload it and release this instance  
  210.     try {  
  211.         if ((servlet != null) &&  
  212.             (wrapper.getAvailable() == Long.MAX_VALUE)) {  
  213.             wrapper.unload();  
  214.         }  
  215.     } catch (Throwable e) {  
  216.         ExceptionUtils.handleThrowable(e);  
  217.         container.getLogger().error(sm.getString("standardWrapper.unloadException",  
  218.                          wrapper.getName()), e);  
  219.         if (throwable == null) {  
  220.             throwable = e;  
  221.             exception(request, response, e);  
  222.         }  
  223.     }  
  224.     long t2=System.currentTimeMillis();  
  225.   
  226.     long time=t2-t1;  
  227.     processingTime += time;  
  228.     if( time > maxTime) maxTime=time;  
  229.     if( time < minTime) minTime=time;  
  230.   
  231. }  

因为要支持Servlet3的新特性及各种异常处理,这段代码显得比较长。关注重点第42行,这里会调用StandardWrapper的allocate方法,不再贴出这个方法的代码,需要提醒的是在allocate方法中可能会调用loadServlet()方法,这就是上一段提到的请求访问web应用而第一次调用该Servlet时再加载并初始化Servlet。

第87到91行会构造一个过滤器链(filterChain)用于执行这一次请求所经过的相应Filter,第111和第128行会调用该filterChain的doFilter方法:

Java代码  收藏代码
  1. public void doFilter(ServletRequest request, ServletResponse response)  
  2.     throws IOException, ServletException {  
  3.   
  4.     if( Globals.IS_SECURITY_ENABLED ) {  
  5.         final ServletRequest req = request;  
  6.         final ServletResponse res = response;  
  7.         try {  
  8.             java.security.AccessController.doPrivileged(  
  9.                 new java.security.PrivilegedExceptionAction<Void>() {  
  10.                     @Override  
  11.                     public Void run()   
  12.                         throws ServletException, IOException {  
  13.                         internalDoFilter(req,res);  
  14.                         return null;  
  15.                     }  
  16.                 }  
  17.             );  
  18.         } catch( PrivilegedActionException pe) {  
  19.             Exception e = pe.getException();  
  20.             if (e instanceof ServletException)  
  21.                 throw (ServletException) e;  
  22.             else if (e instanceof IOException)  
  23.                 throw (IOException) e;  
  24.             else if (e instanceof RuntimeException)  
  25.                 throw (RuntimeException) e;  
  26.             else  
  27.                 throw new ServletException(e.getMessage(), e);  
  28.         }  
  29.     } else {  
  30.         internalDoFilter(request,response);  
  31.     }  
  32. }  

在该方法最后调用了internalDoFilter方法:

Java代码  收藏代码
  1. private void internalDoFilter(ServletRequest request,   
  2.                               ServletResponse response)  
  3.     throws IOException, ServletException {  
  4.   
  5.     // Call the next filter if there is one  
  6.     if (pos < n) {  
  7.         ApplicationFilterConfig filterConfig = filters[pos++];  
  8.         Filter filter = null;  
  9.         try {  
  10.             filter = filterConfig.getFilter();  
  11.             support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,  
  12.                                       filter, request, response);  
  13.               
  14.             if (request.isAsyncSupported() && "false".equalsIgnoreCase(  
  15.                     filterConfig.getFilterDef().getAsyncSupported())) {  
  16.                 request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,  
  17.                         Boolean.FALSE);  
  18.             }  
  19.             if( Globals.IS_SECURITY_ENABLED ) {  
  20.                 final ServletRequest req = request;  
  21.                 final ServletResponse res = response;  
  22.                 Principal principal =   
  23.                     ((HttpServletRequest) req).getUserPrincipal();  
  24.   
  25.                 Object[] args = new Object[]{req, res, this};  
  26.                 SecurityUtil.doAsPrivilege  
  27.                     ("doFilter", filter, classType, args, principal);  
  28.                   
  29.             } else {    
  30.                 filter.doFilter(request, response, this);  
  31.             }  
  32.   
  33.             support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,  
  34.                                       filter, request, response);  
  35.         } catch (IOException e) {  
  36.             if (filter != null)  
  37.                 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,  
  38.                                           filter, request, response, e);  
  39.             throw e;  
  40.         } catch (ServletException e) {  
  41.             if (filter != null)  
  42.                 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,  
  43.                                           filter, request, response, e);  
  44.             throw e;  
  45.         } catch (RuntimeException e) {  
  46.             if (filter != null)  
  47.                 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,  
  48.                                           filter, request, response, e);  
  49.             throw e;  
  50.         } catch (Throwable e) {  
  51.             e = ExceptionUtils.unwrapInvocationTargetException(e);  
  52.             ExceptionUtils.handleThrowable(e);  
  53.             if (filter != null)  
  54.                 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,  
  55.                                           filter, request, response, e);  
  56.             throw new ServletException  
  57.               (sm.getString("filterChain.filter"), e);  
  58.         }  
  59.         return;  
  60.     }  
  61.   
  62.     // We fell off the end of the chain -- call the servlet instance  
  63.     try {  
  64.         if (ApplicationDispatcher.WRAP_SAME_OBJECT) {  
  65.             lastServicedRequest.set(request);  
  66.             lastServicedResponse.set(response);  
  67.         }  
  68.   
  69.         support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,  
  70.                                   servlet, request, response);  
  71.         if (request.isAsyncSupported()  
  72.                 && !support.getWrapper().isAsyncSupported()) {  
  73.             request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,  
  74.                     Boolean.FALSE);  
  75.         }  
  76.         // Use potentially wrapped request from this point  
  77.         if ((request instanceof HttpServletRequest) &&  
  78.             (response instanceof HttpServletResponse)) {  
  79.                   
  80.             if( Globals.IS_SECURITY_ENABLED ) {  
  81.                 final ServletRequest req = request;  
  82.                 final ServletResponse res = response;  
  83.                 Principal principal =   
  84.                     ((HttpServletRequest) req).getUserPrincipal();  
  85.                 Object[] args = new Object[]{req, res};  
  86.                 SecurityUtil.doAsPrivilege("service",  
  87.                                            servlet,  
  88.                                            classTypeUsedInService,   
  89.                                            args,  
  90.                                            principal);     
  91.             } else {    
  92.                 servlet.service(request, response);  
  93.             }  
  94.         } else {  
  95.             servlet.service(request, response);  
  96.         }  
  97.         support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,  
  98.                                   servlet, request, response);  
  99.     } catch (IOException e) {  
  100.         support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,  
  101.                                   servlet, request, response, e);  
  102.         throw e;  
  103.     } catch (ServletException e) {  
  104.         support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,  
  105.                                   servlet, request, response, e);  
  106.         throw e;  
  107.     } catch (RuntimeException e) {  
  108.         support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,  
  109.                                   servlet, request, response, e);  
  110.         throw e;  
  111.     } catch (Throwable e) {  
  112.         ExceptionUtils.handleThrowable(e);  
  113.         support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,  
  114.                                   servlet, request, response, e);  
  115.         throw new ServletException  
  116.           (sm.getString("filterChain.servlet"), e);  
  117.     } finally {  
  118.         if (ApplicationDispatcher.WRAP_SAME_OBJECT) {  
  119.             lastServicedRequest.set(null);  
  120.             lastServicedResponse.set(null);  
  121.         }  
  122.     }  
  123.   
  124. }  

概述一下这段代码,第6到60行是执行过滤器链中的各个过滤器的doFilter方法,实例变量n表示过滤器链中所有的过滤器,pos表示当前要执行的过滤器。其中第7行取出当前要执行的Filter,之后将pos加1,接着第30行执行Filter的doFilter方法。一般的过滤器实现中在最后都会有这一句:

Java代码  收藏代码
  1. FilterChain.doFilter(request, response);  

这样就又回到了filterChain的doFilter方法,形成了一个递归调用。要注意的是,filterChain对象内部的pos是不断加的,所以假如过滤器链中的各个Filter的doFilter方法都执行完之后将会执行到第63行,在接下来的第92行、第95行即调用Servlet的service方法。

该文转载于http://tyrion.iteye.com/blog/1944285  谢谢您的指导!

抱歉!评论已关闭.