浅析Tomcat之StandardWrapperValve

StandardWrapper代表的是一个Servlet,那么它也就是容器调用栈中的最终点.Http请求到达后进行的处理也不单单只是调用了Servlet的方法.它还经过了一层的过滤器.它对外服务与上层容器类似,也是调用了基础阀的方法.那么所有这些操作也是在StandardWrapperValve这个阀中发生的.

首先看下StandardWrapperValve的invoke方法.下面是简要抽出的方法实现.

public final void invoke(Request request, Response response)  
    throws IOException, ServletException {  
  
    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;  
    }  
  
    servlet = wrapper.allocate();  
  
    // Create the filter chain for this request  
    ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();  
    ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet);  
  
    filterChain.doFilter(request.getRequest(), response.getResponse());  
  
    // 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();  
        }  
    }  
  
    wrapper.deallocate(servlet);  
  
    // If this servlet has been marked permanently unavailable,  
    // unload it and release this instance  
    if ((servlet != null) && (wrapper.getAvailable() == Long.MAX_VALUE)) {  
        wrapper.unload();  
    }  
}  

这个方法主要的流程是先确认Context是否是可使用的,如果可以使用的话就allocate一个servlet ,然后用他们创建一个ApplicationFilterChain,接着调用doFilter方法. 最后进行ApplicationFilterChain的回收利用,和servlet的deallocate及wrapper的unload就此结束请求.上面的方法似乎也没有对Servlet的service方法的调用.其实不然且看下面分析.

public ApplicationFilterChain createFilterChain  
    (ServletRequest request, Wrapper wrapper, Servlet servlet) {  
  
    .....  
  
    filterChain.setServlet(servlet);  
  
    // Acquire the filter mappings for this Context  
    StandardContext context = (StandardContext) wrapper.getParent();  
    FilterMap filterMaps[] = context.findFilterMaps();  
  
    // If there are no filter mappings, we are done  
    if ((filterMaps == null) || (filterMaps.length == 0))  
        return (filterChain);  
  
    // Acquire the information we will need to match filter mappings  
    String servletName = wrapper.getName();  
  
    // Add the relevant path-mapped filters to this filter chain  
    for (int i = 0; i < filterMaps.length; i++) {  
        if (!matchDispatcher(filterMaps[i] ,dispatcher)) {  
            continue;  
        }  
        if (!matchFiltersURL(filterMaps[i], requestPath))  
            continue;  
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)  
            context.findFilterConfig(filterMaps[i].getFilterName());  
        if (filterConfig == null) {  
            // FIXME - log configuration problem  
            continue;  
        }  
        boolean isCometFilter = false;  
        if (comet) {  
            try {  
                isCometFilter = filterConfig.getFilter() instanceof CometFilter;  
            } catch (Exception e) {  
                // Note: The try catch is there because getFilter has a lot of  
                // declared exceptions. However, the filter is allocated much  
                // earlier  
                Throwable t = ExceptionUtils.unwrapInvocationTargetException(e);  
                ExceptionUtils.handleThrowable(t);  
            }  
            if (isCometFilter) {  
                filterChain.addFilter(filterConfig);  
            }  
        } else {  
            filterChain.addFilter(filterConfig);  
        }  
    }  
  
    // Add filters that match on servlet name second  
    for (int i = 0; i < filterMaps.length; i++) {  
        if (!matchDispatcher(filterMaps[i] ,dispatcher)) {  
            continue;  
        }  
        if (!matchFiltersServlet(filterMaps[i], servletName))  
            continue;  
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)  
            context.findFilterConfig(filterMaps[i].getFilterName());  
        if (filterConfig == null) {  
            // FIXME - log configuration problem  
            continue;  
        }  
        boolean isCometFilter = false;  
        if (comet) {  
            try {  
                isCometFilter = filterConfig.getFilter() instanceof CometFilter;  
            } catch (Exception e) {  
                // Note: The try catch is there because getFilter has a lot of  
                // declared exceptions. However, the filter is allocated much  
                // earlier  
            }  
            if (isCometFilter) {  
                filterChain.addFilter(filterConfig);  
            }  
        } else {  
            filterChain.addFilter(filterConfig);  
        }  
    }  
  
    // Return the completed filter chain  
    return (filterChain);  
  
}  

从上面的代码中我们可以看出createFilterChain构造出了一个过滤器和Servlet的集合体那就是ApplicationFilterChain.接下来的就是其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() {  
                    @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);  
    }  
}  
  
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 (....) {  
            if (filter != null)  
                support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,  
                                          filter, request, response, e);  
            throw 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 (....) {  
        support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,  
                                  servlet, request, response, e);  
        throw e;  
    } finally {  
        if (ApplicationDispatcher.WRAP_SAME_OBJECT) {  
            lastServicedRequest.set(null);  
            lastServicedResponse.set(null);  
        }  
    }  
  
}  

上述代码中我们可以看出来doFilter调用了内部的一个私有方法.该方法就是对filter进行逐个调用,触法其生命周期事件.最后调用了Servlet的service方法.Service方法后面发生了什么,作为一个web开发者应该都很清楚.至此,请求处理结束.所有的内容都是以Response返回的.



哇,2 个人说话了

  1. 正在研究tomcat7,正好有用,辛苦了。
    如果配个图,就更好,更形象了。

  2. 以前是有画时序图什么的,后面空间没续费,直接被清理了.

留个爪印