Tomcat源码解析系列(十八)Valve#invoke

前言
上篇文章中讲到了 CoyoteAdapter 的 asyncDispatch 和 service 方法处理请求的过程,其中最重要的就是调用了 Engine 里的 Pipeline 对象的 Valve 对象的 invoke 方法,也就是 StandardEngineValve#invoke 方法。Valve 对象相关介绍在这篇文章里提到过,这里就不多说了。


1. StandardEngineValve#invoke

/**
 * Select the appropriate child Host to process this request,
 * based on the requested server name.  If no matching Host can
 * be found, return an appropriate HTTP error.
 *
 * @param request Request to be processed
 * @param response Response to be produced
 *
 * @exception IOException if an input/output error occurred
 * @exception ServletException if a servlet error occurred
 */
@Override
public final void invoke(Request request, Response response)
    throws IOException, ServletException {

    // Select the Host to be used for this Request
    Host host = request.getHost();
    if (host == null) {
        // HTTP 0.9 or HTTP 1.0 request without a host when no default host
        // is defined. This is handled by the CoyoteAdapter.
        return;
    }
    if (request.isAsyncSupported()) {
        request.setAsyncSupported(host.getPipeline().isAsyncSupported());
    }

    // Ask this Host to process this request
    host.getPipeline().getFirst().invoke(request, response);
}

StandardEngineValve#invoke 比较短,逻辑简单。
先调用 Host host = request.getHost() 获取一个 Host 对象,获取不到就直接返回。

/**
 * @return the Host within which this Request is being processed.
 */
public Host getHost() {
    return mappingData.host;
}
/**
 * Mapping data.
 */
protected final MappingData mappingData = new MappingData();

request.getHost() 返回的是 Request 里 MappingData 属性对象的 host 属性,这个属性是在上篇文章提到的 CoyoteAdpater#postParseRequest 方法里赋值的。postParseRequest 里调用了 Service 里的 Mapper 属性对象的 map 方法来解析请求并将 Host 对象关联到 Request 的。

public class MappingData {

    public Host host = null;
    public Context context = null;
    public int contextSlashCount = 0;
    public Context[] contexts = null;
    public Wrapper wrapper = null;
    public boolean jspWildCard = false;

    public final MessageBytes contextPath = MessageBytes.newInstance();
    public final MessageBytes requestPath = MessageBytes.newInstance();
    public final MessageBytes wrapperPath = MessageBytes.newInstance();
    public final MessageBytes pathInfo = MessageBytes.newInstance();

    public final MessageBytes redirectPath = MessageBytes.newInstance();

    // Fields used by ApplicationMapping to implement javax.servlet.http.Mapping
    public MappingMatch matchType = null;

    // methods
    ……
}

MappingData 里保存了处理 Request 需要组件,比如 Host,Context、Wrapper 等,这些属性的初始化都是在 Mapper#map 里完成的。

然后调用 host.getPipeline().getFirst().invoke(request, response),也就是调用 Host 对象里的 Pipeline 属性对象里的 Valve 对象的 invoke 方法,即 StandardHostValve#invoke 方法。

2. StandardHostValve#invoke

/**
 * Select the appropriate child Context to process this request,
 * based on the specified request URI.  If no matching Context can
 * be found, return an appropriate HTTP error.
 *
 * @param request Request to be processed
 * @param response Response to be produced
 *
 * @exception IOException if an input/output error occurred
 * @exception ServletException if a servlet error occurred
 */
@Override
public final void invoke(Request request, Response response)
    throws IOException, ServletException {

    // Select the Context to be used for this Request
    Context context = request.getContext();
    if (context == null) {
        return;
    }

    if (request.isAsyncSupported()) {
        request.setAsyncSupported(context.getPipeline().isAsyncSupported());
    }

    boolean asyncAtStart = request.isAsync();

    try {
        context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);

        if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) {
            // Don't fire listeners during async processing (the listener
            // fired for the request that called startAsync()).
            // If a request init listener throws an exception, the request
            // is aborted.
            return;
        }

        // Ask this Context to process this request. Requests that are
        // already in error must have been routed here to check for
        // application defined error pages so DO NOT forward them to the the
        // application for processing.
        try {
            if (!response.isErrorReportRequired()) {
                context.getPipeline().getFirst().invoke(request, response);
            }
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            container.getLogger().error("Exception Processing " + request.getRequestURI(), t);
            // If a new error occurred while trying to report a previous
            // error allow the original error to be reported.
            if (!response.isErrorReportRequired()) {
                request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
                throwable(request, response, t);
            }
        }

        // Now that the request/response pair is back under container
        // control lift the suspension so that the error handling can
        // complete and/or the container can flush any remaining data
        response.setSuspended(false);

        Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);

        // Protect against NPEs if the context was destroyed during a
        // long running request.
        if (!context.getState().isAvailable()) {
            return;
        }

        // Look for (and render if found) an application level error page
        if (response.isErrorReportRequired()) {
            // If an error has occurred that prevents further I/O, don't waste time
            // producing an error report that will never be read
            AtomicBoolean result = new AtomicBoolean(false);
            response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, result);
            if (result.get()) {
                if (t != null) {
                    throwable(request, response, t);
                } else {
                    status(request, response);
                }
            }
        }

        if (!request.isAsync() && !asyncAtStart) {
            context.fireRequestDestroyEvent(request.getRequest());
        }
    } finally {
        // Access a session (if present) to update last accessed time, based
        // on a strict interpretation of the specification
        if (ACCESS_SESSION) {
            request.getSession(false);
        }

        context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
    }
}

首先调用了 Context context = request.getContext(),获取一个 Context 对象,获取不到就直接返回,这一点跟 StandardEngineValve#invoke 方法第一步类似。

/**
 * Return the Context within which this Request is being processed.
 * 

* This is available as soon as the appropriate Context is identified. * Note that availability of a Context allows getContextPath() * to return a value, and thus enables parsing of the request URI. * * @return the Context mapped with the request */ public Context getContext() { return mappingData.context; } /** * Mapping data. */ protected final MappingData mappingData = new MappingData();

request.getContext() 返回的就是 Request 里的 MappingData 对象的 context 属性,这个属性也是在 CoyoteAdpater#postParseRequest 里解析并赋值的。

然后进入try-catch 语句,并在里面调用了

if (!response.isErrorReportRequired()) {
    context.getPipeline().getFirst().invoke(request, response);
}

这一句也跟 StandardEngineValve#invoke 方法里的类似,就是调用 Context 里的 Pipeline 属性对象的 Valve 属性的 invoke 方法,也就是调用 StandardContextValve#invoke 方法。

再然后就是一些状态设置、触发监听器等收尾工作了。

3. StandardContextValve#invoke

/**
 * Select the appropriate child Wrapper to process this request,
 * based on the specified request URI.  If no matching Wrapper can
 * be found, return an appropriate HTTP error.
 *
 * @param request Request to be processed
 * @param response Response to be produced
 *
 * @exception IOException if an input/output error occurred
 * @exception ServletException if a servlet error occurred
 */
@Override
public final void invoke(Request request, Response response)
    throws IOException, ServletException {

    // Disallow any direct access to resources under WEB-INF or META-INF
    MessageBytes requestPathMB = request.getRequestPathMB();
    if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
            || (requestPathMB.equalsIgnoreCase("/META-INF"))
            || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
            || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    }

    // Select the Wrapper to be used for this Request
    Wrapper wrapper = request.getWrapper();
    if (wrapper == null || wrapper.isUnavailable()) {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    }

    // Acknowledge the request
    try {
        response.sendAcknowledgement();
    } catch (IOException ioe) {
        container.getLogger().error(sm.getString(
                "standardContextValve.acknowledgeException"), ioe);
        request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe);
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        return;
    }

    if (request.isAsyncSupported()) {
        request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
    }
    wrapper.getPipeline().getFirst().invoke(request, response);
}

首先调用 request.getWrapper() 获取一个 Wrapper 对象。

/**
 * @return the Wrapper within which this Request is being processed.
 */
public Wrapper getWrapper() {
    return mappingData.wrapper;
}

/**
 * Mapping data.
 */
protected final MappingData mappingData = new MappingData();

这个对象也是从 Request 里的 MappingData 里获取的,它的初始化跟 MappingData 里的 Host 和 Context 一样。

然后调用 response.sendAcknowledgement() 方法

最后调用 wrapper.getPipeline().getFirst().invoke(request, response),也就是调用 StandardWrapper#invoke 方法。

整个流程跟 StandardEngineValve#invoke、和 StandardHostValve#invoke 类似,不过中间多了一步调用response.sendAcknowledgement() 方法。response.sendAcknowledgement() 最终会调用 Http11Processor#ack() 方法

@Override
protected final void ack() {
    // Acknowledge request
    // Send a 100 status back if it makes sense (response not committed
    // yet, and client specified an expectation for 100-continue)
    if (!response.isCommitted() && request.hasExpectation()) {
        inputBuffer.setSwallowInput(true);
        try {
            outputBuffer.sendAck();
        } catch (IOException e) {
            setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
        }
    }
}

Http11Processor#ack() 也只是简单地调用 outputBuffer.sendAck(),也就是调用 Http11OutputBuffer#sendAck 方法

public static final byte[] ACK_BYTES = ByteChunk.convertToBytes("HTTP/1.1 100 " + CRLF + CRLF);

public void sendAck() throws IOException {
    if (!response.isCommitted()) {
        socketWrapper.write(isBlocking(), Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length);
        if (flushBuffer(true)) {
            throw new IOException(sm.getString("iob.failedwrite.ack"));
        }
    }
}

sendAck() 就是简单地将 HTTP/1.1 100 加上回车换行符写给客户端。

4. StandardWrapperValve#invoke

/**
 * Invoke the servlet we are managing, respecting the rules regarding
 * servlet lifecycle and SingleThreadModel support.
 *
 * @param request Request to be processed
 * @param response Response to be produced
 *
 * @exception IOException if an input/output error occurred
 * @exception ServletException if a servlet error occurred
 */
@Override
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.incrementAndGet();
    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;
    }

    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
    ApplicationFilterChain filterChain =
            ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

    // 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()) {
                        request.getAsyncContextInternal().doInternalDispatch();
                    } 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()) {
                    request.getAsyncContextInternal().doInternalDispatch();
                } else {
                    filterChain.doFilter
                        (request.getRequest(), response.getResponse());
                }
            }

        }
    } catch (ClientAbortException | CloseNowException e) {
        if (container.getLogger().isDebugEnabled()) {
            container.getLogger().debug(sm.getString(
                    "standardWrapper.serviceException", wrapper.getName(),
                    context.getName()), 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) {
        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;

}

整段代码很长,大概分为5段
1、第一个 try-catch 之前
2、第一个 try-catch
3、第一个 try-catch 到第二个 try-catch 之间
4、第二个 try-catch
5、第二个 try-catch 之后
首先看 1,这一段代码很简单就是做一些检查工作。
再来看 2,第一个 try-catch。在 try 语句里调用了

wrapper.allocate()

来获取一个对象。

接着看 3,第一个 try-catch 之后先设置了 request 对象的两个属性,然后执行了

ApplicationFilterFactory.createFilterChain(request, wrapper, servlet) 

来获取一个 ApplicationFilterChain 对象。

再接着看 4,获取到 ApplicationFilterChain 对象后就开始调用

request.getAsyncContextInternal().doInternalDispatch() 
或者 
filterChain.doFilter(request.getRequest(), response.getResponse())

具体调用哪一个方法是由request.isAsyncDispatching() 的值决定的,多数情况下是调用 filterChain.doFilter 方法。

最后看 5,调用完 filterChain.doFilter 方法之后就是一些后置处理了,比如资源回收。

在整个方法中,最后关键的就是调用 ApplicationFilterChain#doFilter 方法来处理了,这个方法将在下篇文章中分析,这里就不多讲了。

4.1 StandardWrapper#allocate

/**
 * Stack containing the STM instances.
 */
protected Stack instancePool = null;

/**
 * Allocate an initialized instance of this Servlet that is ready to have
 * its service() method called.  If the servlet class does
 * not implement SingleThreadModel, the (only) initialized
 * instance may be returned immediately.  If the servlet class implements
 * SingleThreadModel, the Wrapper implementation must ensure
 * that this instance is not allocated again until it is deallocated by a
 * call to deallocate().
 *
 * @exception ServletException if the servlet init() method threw
 *  an exception
 * @exception ServletException if a loading error occurs
 */
@Override
public Servlet allocate() throws ServletException {

    // If we are currently unloading this servlet, throw an exception
    if (unloading) {
        throw new ServletException(sm.getString("standardWrapper.unloading", getName()));
    }

    boolean newInstance = false;

    // If not SingleThreadedModel, return the same instance every time
    if (!singleThreadModel) {
        // Load and initialize our instance if necessary
        if (instance == null || !instanceInitialized) {
            synchronized (this) {
                if (instance == null) {
                    try {
                        if (log.isDebugEnabled()) {
                            log.debug("Allocating non-STM instance");
                        }

                        // Note: We don't know if the Servlet implements
                        // SingleThreadModel until we have loaded it.
                        instance = loadServlet();
                        newInstance = true;
                        if (!singleThreadModel) {
                            // For non-STM, increment here to prevent a race
                            // condition with unload. Bug 43683, test case
                            // #3
                            countAllocated.incrementAndGet();
                        }
                    } catch (ServletException e) {
                        throw e;
                    } catch (Throwable e) {
                        ExceptionUtils.handleThrowable(e);
                        throw new ServletException(sm.getString("standardWrapper.allocate"), e);
                    }
                }
                if (!instanceInitialized) {
                    initServlet(instance);
                }
            }
        }

        if (singleThreadModel) {
            if (newInstance) {
                // Have to do this outside of the sync above to prevent a
                // possible deadlock
                synchronized (instancePool) {
                    instancePool.push(instance);
                    nInstances++;
                }
            }
        } else {
            if (log.isTraceEnabled()) {
                log.trace("  Returning non-STM instance");
            }
            // For new instances, count will have been incremented at the
            // time of creation
            if (!newInstance) {
                countAllocated.incrementAndGet();
            }
            return instance;
        }
    }

    synchronized (instancePool) {
        while (countAllocated.get() >= nInstances) {
            // Allocate a new instance if possible, or else wait
            if (nInstances < maxInstances) {
                try {
                    instancePool.push(loadServlet());
                    nInstances++;
                } catch (ServletException e) {
                    throw e;
                } catch (Throwable e) {
                    ExceptionUtils.handleThrowable(e);
                    throw new ServletException(sm.getString("standardWrapper.allocate"), e);
                }
            } else {
                try {
                    instancePool.wait();
                } catch (InterruptedException e) {
                    // Ignore
                }
            }
        }
        if (log.isTraceEnabled()) {
            log.trace("  Returning allocated STM instance");
        }
        countAllocated.incrementAndGet();
        return instancePool.pop();
    }
}

整段代码主要在 if (!singleThreadModel) 和 synchronized (instancePool) 里。

/**
 * Does this servlet implement the SingleThreadModel interface?
 */
protected volatile boolean singleThreadModel = false;

singleThreadModel 属性是判断 Servlet 实现类是不是实现了 SingleThreadModel 接口。

if (!singleThreadModel) 里的罗就就是如果不是单线程模式的话就每次都返回同样的对象 instance。

synchronized (instancePool) 里的逻辑是如果已经分配使用的 Servlet 对象的数量达到了 instancePool 里的总数 nInstances 的值,但没有超过允许的 maxInstances 数量,就重新调用 loadServlet 加在一个 Servlet 对象,并把它放入 instancePool 里,如果超过了 maxInstances 就调用 instancePool.wait() 方法等待有 instancePool 里有新的空闲的 Servlet 实例。最后调用 instancePool.pop() 方法从 instancePool 取出一个 Servlet实例返回。

4.2 ApplicationFilterFactory#createFilterChain

/**
 * Construct a FilterChain implementation that will wrap the execution of
 * the specified servlet instance.
 *
 * @param request The servlet request we are processing
 * @param wrapper The wrapper managing the servlet instance
 * @param servlet The servlet instance to be wrapped
 *
 * @return The configured FilterChain instance or null if none is to be
 *         executed.
 */
public static ApplicationFilterChain createFilterChain(ServletRequest request,
        Wrapper wrapper, Servlet servlet) {

    // If there is no servlet to execute, return null
    if (servlet == null)
        return null;

    // Create and initialize a filter chain object
    ApplicationFilterChain filterChain = null;
    if (request instanceof Request) {
        Request req = (Request) request;
        if (Globals.IS_SECURITY_ENABLED) {
            // Security: Do not recycle
            filterChain = new ApplicationFilterChain();
        } else {
            filterChain = (ApplicationFilterChain) req.getFilterChain();
            if (filterChain == null) {
                filterChain = new ApplicationFilterChain();
                req.setFilterChain(filterChain);
            }
        }
    } else {
        // Request dispatcher in use
        filterChain = new ApplicationFilterChain();
    }

    filterChain.setServlet(servlet);
    filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());

    // 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
    DispatcherType dispatcher =
            (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

    String requestPath = null;
    Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
    if (attribute != null){
        requestPath = attribute.toString();
    }

    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;
        }
        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;
        }
        filterChain.addFilter(filterConfig);
    }

    // Return the completed filter chain
    return filterChain;
}

createFilterChain 方法里,先 new 一个 ApplicationFilterChain 对象,或者调用 req.getFilterChain() 获取一个。
然后调用

FilterMap filterMaps[] = context.findFilterMaps();

获取 FilterMap 类型的数组,这个 FilterMap 是

/**
 * The set of filter mappings for this application, in the order
 * they were defined in the deployment descriptor with additional mappings
 * added via the {@link ServletContext} possibly both before and after those
 * defined in the deployment descriptor.
 */
private final ContextFilterMaps filterMaps = new ContextFilterMaps();

/**
 * @return the set of filter mappings for this Context.
 */
@Override
public FilterMap[] findFilterMaps() {
    return filterMaps.asArray();
}

FilterMap 属性是 Context 用来保存应用程序里的 web.xml 文件里配置的 Filter 的相关信息的,比如名字、作用路径等。

拿到 FilterMap[] 数组后,就是两个 for 循环,这两个 for 循环里的内容是一样的。首先分别调用

matchDispatcher(filterMaps[i] ,dispatcher)
matchFiltersURL(filterMaps[i], requestPath)

来判断 FilterMap 关联的 Filter 的属性是否能匹配上,如果能匹配上,就调用

ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName());

来获取一个 ApplicationFilterConfig 对象,并调用filterChain.addFilter(filterConfig) 把这个对象加入到 filterChain 里。

4.2.1 StandardContext#findFilterConfig

/**
 * The set of filter definitions for this application, keyed by
 * filter name.
 */
private Map filterDefs = new HashMap<>();


/**
 * The set of filter configurations (and associated filter instances) we
 * have initialized, keyed by filter name.
 */
private Map filterConfigs = new HashMap<>();

/**
 * Find and return the initialized FilterConfig for the
 * specified filter name, if any; otherwise return null.
 *
 * @param name Name of the desired filter
 * @return the filter config object
 */
public FilterConfig findFilterConfig(String name) {
    return filterConfigs.get(name);
}

filterConfigs 这个 map 是在 StandardContext#filterStart 方法里放入 ApplicationFilterConfig 对象的。

/**
 * The set of filter definitions for this application, keyed by
 * filter name.
 */
private Map filterDefs = new HashMap<>();

/**
 * Configure and initialize the set of filters for this Context.
 * @return true if all filter initialization completed
 * successfully, or false otherwise.
 */
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();
        for (Entry entry : filterDefs.entrySet()) {
            String name = entry.getKey();
            if (getLogger().isDebugEnabled()) {
                getLogger().debug(" Starting filter '" + name + "'");
            }
            try {
                ApplicationFilterConfig filterConfig =
                        new ApplicationFilterConfig(this, entry.getValue());
                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;
}

filterStart() 方法将 filterDefs 里定义的 Filter 放入到创建的 ApplicationFilterConfig 对象中,并把 ApplicationFilterConfig 对象存在 filterConfigs 这个 map 里。filterStart() 是在 Context#startInternal 里调用的。

在 Context 初始化过程中,会对 web.xml 里定义的 Filter 进行解析,并把解析结果放在 filterDefs 这个 map 里。


小结
本文分析了 StandardEngineValve、StandardHostValve、StandardContextValve 和 StandardWrapperValve 的 invoke 方法,这三个 Valve 的 invoke 方法的核心逻辑就是调用子容器的 Pipeline 的 Valve 的invoke 方法,也就是 StandardEngineValve#invoke -> StandardHostValve#invoke -> StandardContextValve#invoke -> StandardWrapper#invoke 方法。而 StandardWrapper#invoke 最终调用 ApplicationFilterChain#doFilter 方法来处理请求。

你可能感兴趣的:(java,tomcat)