前一篇文章分析到了org.apache.catalina.deploy.WebXml类的configureContext方法,可以看到在这个方法中通过各种setXXX、addXXX方法的调用,使得每个应用中的web.xml文件的解析后将应用内部的表示Servlet、Listener、Filter的配置信息与表示一个web应用的Context对象关联起来。
这里列出configureContext方法中与Servlet、Listener、Filter的配置信息设置相关的调用代码:
- for (FilterDef filter : filters.values()) {
- if (filter.getAsyncSupported() == null) {
- filter.setAsyncSupported("false");
- }
- context.addFilterDef(filter);
- }
- for (FilterMap filterMap : filterMaps) {
- context.addFilterMap(filterMap);
- }
这是设置Filter相关配置信息的。
- for (String listener : listeners) {
- context.addApplicationListener(
- new ApplicationListener(listener, false));
- }
这是给应用添加Listener的。
- for (ServletDef servlet : servlets.values()) {
- Wrapper wrapper = context.createWrapper();
- // Description is ignored
- // Display name is ignored
- // Icons are ignored
- // jsp-file gets passed to the JSP Servlet as an init-param
- if (servlet.getLoadOnStartup() != null) {
- wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
- }
- if (servlet.getEnabled() != null) {
- wrapper.setEnabled(servlet.getEnabled().booleanValue());
- }
- wrapper.setName(servlet.getServletName());
- Map<String,String> params = servlet.getParameterMap();
- for (Entry<String, String> entry : params.entrySet()) {
- wrapper.addInitParameter(entry.getKey(), entry.getValue());
- }
- wrapper.setRunAs(servlet.getRunAs());
- Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
- for (SecurityRoleRef roleRef : roleRefs) {
- wrapper.addSecurityReference(
- roleRef.getName(), roleRef.getLink());
- }
- wrapper.setServletClass(servlet.getServletClass());
- MultipartDef multipartdef = servlet.getMultipartDef();
- if (multipartdef != null) {
- if (multipartdef.getMaxFileSize() != null &&
- multipartdef.getMaxRequestSize()!= null &&
- multipartdef.getFileSizeThreshold() != null) {
- wrapper.setMultipartConfigElement(new MultipartConfigElement(
- multipartdef.getLocation(),
- Long.parseLong(multipartdef.getMaxFileSize()),
- Long.parseLong(multipartdef.getMaxRequestSize()),
- Integer.parseInt(
- multipartdef.getFileSizeThreshold())));
- } else {
- wrapper.setMultipartConfigElement(new MultipartConfigElement(
- multipartdef.getLocation()));
- }
- }
- if (servlet.getAsyncSupported() != null) {
- wrapper.setAsyncSupported(
- servlet.getAsyncSupported().booleanValue());
- }
- wrapper.setOverridable(servlet.isOverridable());
- context.addChild(wrapper);
- }
- for (Entry<String, String> entry : servletMappings.entrySet()) {
- context.addServletMapping(entry.getKey(), entry.getValue());
- }
这段代码是设置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方法中:
- protected synchronized void startInternal() throws LifecycleException {
- if(log.isDebugEnabled())
- log.debug("Starting " + getBaseName());
- // Send j2ee.state.starting notification
- if (this.getObjectName() != null) {
- Notification notification = new Notification("j2ee.state.starting",
- this.getObjectName(), sequenceNumber.getAndIncrement());
- broadcaster.sendNotification(notification);
- }
- setConfigured(false);
- boolean ok = true;
- // Currently this is effectively a NO-OP but needs to be called to
- // ensure the NamingResources follows the correct lifecycle
- if (namingResources != null) {
- namingResources.start();
- }
- // Add missing components as necessary
- if (webappResources == null) { // (1) Required by Loader
- if (log.isDebugEnabled())
- log.debug("Configuring default Resources");
- try {
- if ((getDocBase() != null) && (getDocBase().endsWith(".war")) &&
- (!(new File(getBasePath())).isDirectory()))
- setResources(new WARDirContext());
- else
- setResources(new FileDirContext());
- } catch (IllegalArgumentException e) {
- log.error("Error initializing resources: " + e.getMessage());
- ok = false;
- }
- }
- if (ok) {
- if (!resourcesStart()) {
- log.error( "Error in resourceStart()");
- ok = false;
- }
- }
- if (getLoader() == null) {
- WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
- webappLoader.setDelegate(getDelegate());
- setLoader(webappLoader);
- }
- // Initialize character set mapper
- getCharsetMapper();
- // Post work directory
- postWorkDirectory();
- // Validate required extensions
- boolean dependencyCheck = true;
- try {
- dependencyCheck = ExtensionValidator.validateApplication
- (getResources(), this);
- } catch (IOException ioe) {
- log.error("Error in dependencyCheck", ioe);
- dependencyCheck = false;
- }
- if (!dependencyCheck) {
- // do not make application available if depency check fails
- ok = false;
- }
- // Reading the "catalina.useNaming" environment variable
- String useNamingProperty = System.getProperty("catalina.useNaming");
- if ((useNamingProperty != null)
- && (useNamingProperty.equals("false"))) {
- useNaming = false;
- }
- if (ok && isUseNaming()) {
- if (getNamingContextListener() == null) {
- NamingContextListener ncl = new NamingContextListener();
- ncl.setName(getNamingContextName());
- ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
- addLifecycleListener(ncl);
- setNamingContextListener(ncl);
- }
- }
- // Standard container startup
- if (log.isDebugEnabled())
- log.debug("Processing standard container startup");
- // Binding thread
- ClassLoader oldCCL = bindThread();
- try {
- if (ok) {
- // Start our subordinate components, if any
- if ((loader != null) && (loader instanceof Lifecycle))
- ((Lifecycle) loader).start();
- // since the loader just started, the webapp classloader is now
- // created.
- // By calling unbindThread and bindThread in a row, we setup the
- // current Thread CCL to be the webapp classloader
- unbindThread(oldCCL);
- oldCCL = bindThread();
- // Initialize logger again. Other components might have used it
- // too early, so it should be reset.
- logger = null;
- getLogger();
- if ((cluster != null) && (cluster instanceof Lifecycle))
- ((Lifecycle) cluster).start();
- Realm realm = getRealmInternal();
- if ((realm != null) && (realm instanceof Lifecycle))
- ((Lifecycle) realm).start();
- if ((resources != null) && (resources instanceof Lifecycle))
- ((Lifecycle) resources).start();
- // Notify our interested LifecycleListeners
- fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
- // Start our child containers, if not already started
- for (Container child : findChildren()) {
- if (!child.getState().isAvailable()) {
- child.start();
- }
- }
- // Start the Valves in our pipeline (including the basic),
- // if any
- if (pipeline instanceof Lifecycle) {
- ((Lifecycle) pipeline).start();
- }
- // Acquire clustered manager
- Manager contextManager = null;
- if (manager == null) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("standardContext.cluster.noManager",
- Boolean.valueOf((getCluster() != null)),
- Boolean.valueOf(distributable)));
- }
- if ( (getCluster() != null) && distributable) {
- try {
- contextManager = getCluster().createManager(getName());
- } catch (Exception ex) {
- log.error("standardContext.clusterFail", ex);
- ok = false;
- }
- } else {
- contextManager = new StandardManager();
- }
- }
- // Configure default manager if none was specified
- if (contextManager != null) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("standardContext.manager",
- contextManager.getClass().getName()));
- }
- setManager(contextManager);
- }
- if (manager!=null && (getCluster() != null) && distributable) {
- //let the cluster know that there is a context that is distributable
- //and that it has its own manager
- getCluster().registerManager(manager);
- }
- }
- } finally {
- // Unbinding thread
- unbindThread(oldCCL);
- }
- if (!getConfigured()) {
- log.error( "Error getConfigured");
- ok = false;
- }
- // We put the resources into the servlet context
- if (ok)
- getServletContext().setAttribute
- (Globals.RESOURCES_ATTR, getResources());
- // Initialize associated mapper
- mapper.setContext(getPath(), welcomeFiles, resources);
- // Binding thread
- oldCCL = bindThread();
- if (ok ) {
- if (getInstanceManager() == null) {
- javax.naming.Context context = null;
- if (isUseNaming() && getNamingContextListener() != null) {
- context = getNamingContextListener().getEnvContext();
- }
- Map<String, Map<String, String>> injectionMap = buildInjectionMap(
- getIgnoreAnnotations() ? new NamingResources(): getNamingResources());
- setInstanceManager(new DefaultInstanceManager(context,
- injectionMap, this, this.getClass().getClassLoader()));
- getServletContext().setAttribute(
- InstanceManager.class.getName(), getInstanceManager());
- }
- }
- try {
- // Create context attributes that will be required
- if (ok) {
- getServletContext().setAttribute(
- JarScanner.class.getName(), getJarScanner());
- }
- // Set up the context init params
- mergeParameters();
- // Call ServletContainerInitializers
- for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
- initializers.entrySet()) {
- try {
- entry.getKey().onStartup(entry.getValue(),
- getServletContext());
- } catch (ServletException e) {
- log.error(sm.getString("standardContext.sciFail"), e);
- ok = false;
- break;
- }
- }
- // Configure and call application event listeners
- if (ok) {
- if (!listenerStart()) {
- log.error( "Error listenerStart");
- ok = false;
- }
- }
- try {
- // Start manager
- if ((manager != null) && (manager instanceof Lifecycle)) {
- ((Lifecycle) getManager()).start();
- }
- } catch(Exception e) {
- log.error("Error manager.start()", e);
- ok = false;
- }
- // Configure and call application filters
- if (ok) {
- if (!filterStart()) {
- log.error("Error filterStart");
- ok = false;
- }
- }
- // Load and initialize all "load on startup" servlets
- if (ok) {
- loadOnStartup(findChildren());
- }
- // Start ContainerBackgroundProcessor thread
- super.threadStart();
- } finally {
- // Unbinding thread
- unbindThread(oldCCL);
- }
- // Set available status depending upon startup success
- if (ok) {
- if (log.isDebugEnabled())
- log.debug("Starting completed");
- } else {
- log.error(sm.getString("standardContext.startFailed", getName()));
- }
- startTime=System.currentTimeMillis();
- // Send j2ee.state.running notification
- if (ok && (this.getObjectName() != null)) {
- Notification notification =
- new Notification("j2ee.state.running", this.getObjectName(),
- sequenceNumber.getAndIncrement());
- broadcaster.sendNotification(notification);
- }
- // Close all JARs right away to avoid always opening a peak number
- // of files on startup
- if (getLoader() instanceof WebappLoader) {
- ((WebappLoader) getLoader()).closeJARs(true);
- }
- // Reinitializing if something went wrong
- if (!ok) {
- setState(LifecycleState.FAILED);
- } else {
- setState(LifecycleState.STARTING);
- }
- }
这303行可以讲的东西有很多,为了不偏离本文主题只抽出与现在要讨论的问题相关的代码来分析。
第125行会发布一个CONFIGURE_START_EVENT事件,按前一篇博文所述,这里即会触发对web.xml的解析。第205、206行设置实例管理器为DefaultInstanceManager(这个类在后面谈实例构造时会用到)。第237行会调用listenerStart方法,第255行调用了filterStart方法,第263行调用了loadOnStartup方法,这三处调用即触发Listener、Filter、Servlet真正对象的构造,下面逐个分析这些方法。
listenerStart方法的完整代码较长,这里仅列出与Listenner对象构造相关的代码:
- // Instantiate the required listeners
- ApplicationListener listeners[] = applicationListeners;
- Object results[] = new Object[listeners.length];
- boolean ok = true;
- Set<Object> noPluggabilityListeners = new HashSet<Object>();
- for (int i = 0; i < results.length; i++) {
- if (getLogger().isDebugEnabled())
- getLogger().debug(" Configuring event listener class '" +
- listeners[i] + "'");
- try {
- ApplicationListener listener = listeners[i];
- results[i] = instanceManager.newInstance(
- listener.getClassName());
- if (listener.isPluggabilityBlocked()) {
- noPluggabilityListeners.add(results[i]);
- }
- } catch (Throwable t) {
- t = ExceptionUtils.unwrapInvocationTargetException(t);
- ExceptionUtils.handleThrowable(t);
- getLogger().error
- (sm.getString("standardContext.applicationListener",
- listeners[i]), t);
- ok = false;
- }
- }
先从Context对象中取出实例变量applicationListeners(该变量的值在web.xml解析时设置),第12行通过调用instanceManager.newInstance(listener.getClassName()),前面在看StandardContext的startInternal方法第205行时看到instanceManager被设置为DefaultInstanceManager对象,所以这里实际会执行DefaultInstanceManager类的newInstance方法:
- public Object newInstance(String className) throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException, ClassNotFoundException {
- Class<?> clazz = loadClassMaybePrivileged(className, classLoader);
- return newInstance(clazz.newInstance(), clazz);
- }
所以instanceManager.newInstance(listener.getClassName())这段代码的作用是取出web.xml中配置的Listener的class配置信息,从而构造实际配置的Listener对象。
看下filterStart方法:
- public boolean filterStart() {
- if (getLogger().isDebugEnabled())
- getLogger().debug("Starting filters");
- // Instantiate and record a FilterConfig for each defined filter
- boolean ok = true;
- synchronized (filterConfigs) {
- filterConfigs.clear();
- Iterator<String> names = filterDefs.keySet().iterator();
- while (names.hasNext()) {
- String name = names.next();
- if (getLogger().isDebugEnabled())
- getLogger().debug(" Starting filter '" + name + "'");
- ApplicationFilterConfig filterConfig = null;
- try {
- filterConfig =
- new ApplicationFilterConfig(this, filterDefs.get(name));
- filterConfigs.put(name, filterConfig);
- } catch (Throwable t) {
- t = ExceptionUtils.unwrapInvocationTargetException(t);
- ExceptionUtils.handleThrowable(t);
- getLogger().error
- (sm.getString("standardContext.filterStart", name), t);
- ok = false;
- }
- }
- }
- return (ok);
- }
这段代码看起来很简单,取出web.xml解析时读到的filter配置信息,在第17行调用ApplicationFilterConfig了构造方法:
- ApplicationFilterConfig(Context context, FilterDef filterDef)
- throws ClassCastException, ClassNotFoundException,
- IllegalAccessException, InstantiationException,
- ServletException, InvocationTargetException, NamingException {
- super();
- this.context = context;
- this.filterDef = filterDef;
- // Allocate a new filter instance if necessary
- if (filterDef.getFilter() == null) {
- getFilter();
- } else {
- this.filter = filterDef.getFilter();
- getInstanceManager().newInstance(filter);
- initFilter();
- }
- }
默认情况下filterDef中是没有Filter对象的,所以会调用第12行getFilter方法:
- Filter getFilter() throws ClassCastException, ClassNotFoundException,
- IllegalAccessException, InstantiationException, ServletException,
- InvocationTargetException, NamingException {
- // Return the existing filter instance, if any
- if (this.filter != null)
- return (this.filter);
- // Identify the class loader we will be using
- String filterClass = filterDef.getFilterClass();
- this.filter = (Filter) getInstanceManager().newInstance(filterClass);
- initFilter();
- return (this.filter);
- }
与Listener的对象构造类似,都是通过调用getInstanceManager().newInstance方法。当然,按照Servlet规范,第13行还会调用Filter的init方法。
看下loadOnStartup方法:
- public void loadOnStartup(Container children[]) {
- // Collect "load on startup" servlets that need to be initialized
- TreeMap<Integer, ArrayList<Wrapper>> map =
- new TreeMap<Integer, ArrayList<Wrapper>>();
- for (int i = 0; i < children.length; i++) {
- Wrapper wrapper = (Wrapper) children[i];
- int loadOnStartup = wrapper.getLoadOnStartup();
- if (loadOnStartup < 0)
- continue;
- Integer key = Integer.valueOf(loadOnStartup);
- ArrayList<Wrapper> list = map.get(key);
- if (list == null) {
- list = new ArrayList<Wrapper>();
- map.put(key, list);
- }
- list.add(wrapper);
- }
- // Load the collected "load on startup" servlets
- for (ArrayList<Wrapper> list : map.values()) {
- for (Wrapper wrapper : list) {
- try {
- wrapper.load();
- } catch (ServletException e) {
- getLogger().error(sm.getString("standardWrapper.loadException",
- getName()), StandardWrapper.getRootCause(e));
- // NOTE: load errors (including a servlet that throws
- // UnavailableException from tht init() method) are NOT
- // fatal to application startup
- }
- }
- }
- }
在web应用启动时将会加载配置了load-on-startup属性的Servlet。第24行,调用了StandardWrapper类的load方法:
- public synchronized void load() throws ServletException {
- instance = loadServlet();
- if (!instanceInitialized) {
- initServlet(instance);
- }
- if (isJspServlet) {
- StringBuilder oname =
- new StringBuilder(MBeanUtils.getDomain(getParent()));
- oname.append(":type=JspMonitor,name=");
- oname.append(getName());
- oname.append(getWebModuleKeyProperties());
- try {
- jspMonitorON = new ObjectName(oname.toString());
- Registry.getRegistry(null, null)
- .registerComponent(instance, jspMonitorON, null);
- } catch( Exception ex ) {
- log.info("Error registering JSP monitoring with jmx " +
- instance);
- }
- }
- }
在第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方法的代码:
- public final void invoke(Request request, Response response)
- throws IOException, ServletException {
- // Initialize local variables we may need
- boolean unavailable = false;
- Throwable throwable = null;
- // This should be a Request attribute...
- long t1=System.currentTimeMillis();
- requestCount++;
- StandardWrapper wrapper = (StandardWrapper) getContainer();
- Servlet servlet = null;
- Context context = (Context) wrapper.getParent();
- // Check for the application being marked unavailable
- if (!context.getState().isAvailable()) {
- response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
- sm.getString("standardContext.isUnavailable"));
- unavailable = true;
- }
- // Check for the servlet being marked unavailable
- if (!unavailable && wrapper.isUnavailable()) {
- container.getLogger().info(sm.getString("standardWrapper.isUnavailable",
- wrapper.getName()));
- long available = wrapper.getAvailable();
- if ((available > 0L) && (available < Long.MAX_VALUE)) {
- response.setDateHeader("Retry-After", available);
- response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
- sm.getString("standardWrapper.isUnavailable",
- wrapper.getName()));
- } else if (available == Long.MAX_VALUE) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND,
- sm.getString("standardWrapper.notFound",
- wrapper.getName()));
- }
- unavailable = true;
- }
- // Allocate a servlet instance to process this request
- try {
- if (!unavailable) {
- servlet = wrapper.allocate();
- }
- } catch (UnavailableException e) {
- container.getLogger().error(
- sm.getString("standardWrapper.allocateException",
- wrapper.getName()), e);
- long available = wrapper.getAvailable();
- if ((available > 0L) && (available < Long.MAX_VALUE)) {
- response.setDateHeader("Retry-After", available);
- response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
- sm.getString("standardWrapper.isUnavailable",
- wrapper.getName()));
- } else if (available == Long.MAX_VALUE) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND,
- sm.getString("standardWrapper.notFound",
- wrapper.getName()));
- }
- } catch (ServletException e) {
- container.getLogger().error(sm.getString("standardWrapper.allocateException",
- wrapper.getName()), StandardWrapper.getRootCause(e));
- throwable = e;
- exception(request, response, e);
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- container.getLogger().error(sm.getString("standardWrapper.allocateException",
- wrapper.getName()), e);
- throwable = e;
- exception(request, response, e);
- servlet = null;
- }
- // Identify if the request is Comet related now that the servlet has been allocated
- boolean comet = false;
- if (servlet instanceof CometProcessor && request.getAttribute(
- Globals.COMET_SUPPORTED_ATTR) == Boolean.TRUE) {
- comet = true;
- request.setComet(true);
- }
- MessageBytes requestPathMB = request.getRequestPathMB();
- DispatcherType dispatcherType = DispatcherType.REQUEST;
- if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
- request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
- request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
- requestPathMB);
- // Create the filter chain for this request
- ApplicationFilterFactory factory =
- ApplicationFilterFactory.getInstance();
- ApplicationFilterChain filterChain =
- factory.createFilterChain(request, wrapper, servlet);
- // Reset comet flag value after creating the filter chain
- request.setComet(false);
- // Call the filter chain for this request
- // NOTE: This also calls the servlet's service() method
- try {
- if ((servlet != null) && (filterChain != null)) {
- // Swallow output if needed
- if (context.getSwallowOutput()) {
- try {
- SystemLogHandler.startCapture();
- if (request.isAsyncDispatching()) {
- //TODO SERVLET3 - async
- ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();
- } else if (comet) {
- filterChain.doFilterEvent(request.getEvent());
- request.setComet(true);
- } else {
- filterChain.doFilter(request.getRequest(),
- response.getResponse());
- }
- } finally {
- String log = SystemLogHandler.stopCapture();
- if (log != null && log.length() > 0) {
- context.getLogger().info(log);
- }
- }
- } else {
- if (request.isAsyncDispatching()) {
- //TODO SERVLET3 - async
- ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();
- } else if (comet) {
- request.setComet(true);
- filterChain.doFilterEvent(request.getEvent());
- } else {
- filterChain.doFilter
- (request.getRequest(), response.getResponse());
- }
- }
- }
- } catch (ClientAbortException e) {
- throwable = e;
- exception(request, response, e);
- } catch (IOException e) {
- container.getLogger().error(sm.getString(
- "standardWrapper.serviceException", wrapper.getName(),
- context.getName()), e);
- throwable = e;
- exception(request, response, e);
- } catch (UnavailableException e) {
- container.getLogger().error(sm.getString(
- "standardWrapper.serviceException", wrapper.getName(),
- context.getName()), e);
- // throwable = e;
- // exception(request, response, e);
- wrapper.unavailable(e);
- long available = wrapper.getAvailable();
- if ((available > 0L) && (available < Long.MAX_VALUE)) {
- response.setDateHeader("Retry-After", available);
- response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
- sm.getString("standardWrapper.isUnavailable",
- wrapper.getName()));
- } else if (available == Long.MAX_VALUE) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND,
- sm.getString("standardWrapper.notFound",
- wrapper.getName()));
- }
- // Do not save exception in 'throwable', because we
- // do not want to do exception(request, response, e) processing
- } catch (ServletException e) {
- Throwable rootCause = StandardWrapper.getRootCause(e);
- if (!(rootCause instanceof ClientAbortException)) {
- container.getLogger().error(sm.getString(
- "standardWrapper.serviceExceptionRoot",
- wrapper.getName(), context.getName(), e.getMessage()),
- rootCause);
- }
- throwable = e;
- exception(request, response, e);
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- container.getLogger().error(sm.getString(
- "standardWrapper.serviceException", wrapper.getName(),
- context.getName()), e);
- throwable = e;
- exception(request, response, e);
- }
- // Release the filter chain (if any) for this request
- if (filterChain != null) {
- if (request.isComet()) {
- // If this is a Comet request, then the same chain will be used for the
- // processing of all subsequent events.
- filterChain.reuse();
- } else {
- filterChain.release();
- }
- }
- // Deallocate the allocated servlet instance
- try {
- if (servlet != null) {
- wrapper.deallocate(servlet);
- }
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- container.getLogger().error(sm.getString("standardWrapper.deallocateException",
- wrapper.getName()), e);
- if (throwable == null) {
- throwable = e;
- exception(request, response, e);
- }
- }
- // If this servlet has been marked permanently unavailable,
- // unload it and release this instance
- try {
- if ((servlet != null) &&
- (wrapper.getAvailable() == Long.MAX_VALUE)) {
- wrapper.unload();
- }
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- container.getLogger().error(sm.getString("standardWrapper.unloadException",
- wrapper.getName()), e);
- if (throwable == null) {
- throwable = e;
- exception(request, response, e);
- }
- }
- long t2=System.currentTimeMillis();
- long time=t2-t1;
- processingTime += time;
- if( time > maxTime) maxTime=time;
- if( time < minTime) minTime=time;
- }
因为要支持Servlet3的新特性及各种异常处理,这段代码显得比较长。关注重点第42行,这里会调用StandardWrapper的allocate方法,不再贴出这个方法的代码,需要提醒的是在allocate方法中可能会调用loadServlet()方法,这就是上一段提到的请求访问web应用而第一次调用该Servlet时再加载并初始化Servlet。
第87到91行会构造一个过滤器链(filterChain)用于执行这一次请求所经过的相应Filter,第111和第128行会调用该filterChain的doFilter方法:
- public void doFilter(ServletRequest request, ServletResponse response)
- throws IOException, ServletException {
- if( Globals.IS_SECURITY_ENABLED ) {
- final ServletRequest req = request;
- final ServletResponse res = response;
- try {
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedExceptionAction<Void>() {
- @Override
- public Void 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;
- else
- throw new ServletException(e.getMessage(), e);
- }
- } else {
- internalDoFilter(request,response);
- }
- }
在该方法最后调用了internalDoFilter方法:
- private void internalDoFilter(ServletRequest request,
- ServletResponse response)
- throws IOException, ServletException {
- // Call the next filter if there is one
- if (pos < n) {
- ApplicationFilterConfig filterConfig = filters[pos++];
- Filter filter = null;
- try {
- filter = filterConfig.getFilter();
- support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
- filter, request, response);
- if (request.isAsyncSupported() && "false".equalsIgnoreCase(
- filterConfig.getFilterDef().getAsyncSupported())) {
- request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
- Boolean.FALSE);
- }
- if( Globals.IS_SECURITY_ENABLED ) {
- final ServletRequest req = request;
- final ServletResponse res = response;
- Principal principal =
- ((HttpServletRequest) req).getUserPrincipal();
- Object[] args = new Object[]{req, res, this};
- SecurityUtil.doAsPrivilege
- ("doFilter", filter, classType, args, principal);
- } else {
- filter.doFilter(request, response, this);
- }
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, request, response);
- } catch (IOException e) {
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, request, response, e);
- throw e;
- } catch (ServletException e) {
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, request, response, e);
- throw e;
- } catch (RuntimeException e) {
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, request, response, e);
- throw e;
- } catch (Throwable e) {
- e = ExceptionUtils.unwrapInvocationTargetException(e);
- ExceptionUtils.handleThrowable(e);
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
- filter, request, response, e);
- throw new ServletException
- (sm.getString("filterChain.filter"), e);
- }
- return;
- }
- // We fell off the end of the chain -- call the servlet instance
- try {
- if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
- lastServicedRequest.set(request);
- lastServicedResponse.set(response);
- }
- support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
- servlet, request, response);
- if (request.isAsyncSupported()
- && !support.getWrapper().isAsyncSupported()) {
- request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
- Boolean.FALSE);
- }
- // Use potentially wrapped request from this point
- if ((request instanceof HttpServletRequest) &&
- (response instanceof HttpServletResponse)) {
- if( Globals.IS_SECURITY_ENABLED ) {
- final ServletRequest req = request;
- final ServletResponse res = response;
- Principal principal =
- ((HttpServletRequest) req).getUserPrincipal();
- Object[] args = new Object[]{req, res};
- SecurityUtil.doAsPrivilege("service",
- servlet,
- classTypeUsedInService,
- args,
- principal);
- } else {
- servlet.service(request, response);
- }
- } else {
- servlet.service(request, response);
- }
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response);
- } catch (IOException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- throw e;
- } catch (ServletException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- throw e;
- } catch (RuntimeException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- throw e;
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
- servlet, request, response, e);
- throw new ServletException
- (sm.getString("filterChain.servlet"), e);
- } finally {
- if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
- lastServicedRequest.set(null);
- lastServicedResponse.set(null);
- }
- }
- }
概述一下这段代码,第6到60行是执行过滤器链中的各个过滤器的doFilter方法,实例变量n表示过滤器链中所有的过滤器,pos表示当前要执行的过滤器。其中第7行取出当前要执行的Filter,之后将pos加1,接着第30行执行Filter的doFilter方法。一般的过滤器实现中在最后都会有这一句:
- FilterChain.doFilter(request, response);
这样就又回到了filterChain的doFilter方法,形成了一个递归调用。要注意的是,filterChain对象内部的pos是不断加的,所以假如过滤器链中的各个Filter的doFilter方法都执行完之后将会执行到第63行,在接下来的第92行、第95行即调用Servlet的service方法。
顶
踩