Tomcat源码分析三(Tomcat请求源码分析)

一、前言

在调试 Spring MVC 或编写自定义 Filter、Valve 时,我们经常会遇到一些问题:

  • Request 是在什么时候创建的?
  • org.apache.catalina.connector.Request 和 HttpServletRequest 之间是什么关系?
  • 请求是如何一步步交给应用层 Servlet 的?

带着这些问题,本文将以 Tomcat 源码为线索,从 Connector 到 Container,梳理一次 HTTP 请求在 Tomcat 内部的完整执行路径,本文直接是从原始的请求Request开始往下分析,不会具体分析 请求是如果变成Request对象的,本文基于Tomcat9。

二、请求源码分析

上文说到Tomcat在启动时候创建两个线程,一个线程名称是Poller线程、一个是Acceptor线程,Acceptor线程负责接收请求,然后Poller线程负责处理请求中的事件,比如读写事件然后解析Http请求封装为原始的Request对象,这都是网络io的知识,这篇文章我会从封装为Request对象的源码开始分析是如何一步步调用到Servlet的,这块开始的源码是位于CoyoteAdapter类,调用的方法是service,该方法源码如下:

复制代码
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) throws Exception {
    Request request = connector.createRequest();
    request.setCoyoteRequest(req);
    Response response = connector.createResponse();
    response.setCoyoteResponse(res);
    request.setResponse(response);
    response.setRequest(request);
}

通过调用connector.createRequest()方法创建一个Request,它实现了HttpServletRequest对象,实现了Servlet规范,同时把原始的Request对象设置到成员属性org.apache.coyote.Request coyoteRequest上,然后调用connector.createResponse()方法创建一个Response对象,它实现了HttpServletResponse对象,同样是实现了Servlet规范,把原始的Response对象设置到成员属性org.apache.coyote.Response coyoteResponse上,然后分别设置HttpServletRequest的成员属性是HttpServletResponse,HttpServletResponse的成员属性是HttpServletRequest。

然后调用org.apache.catalina.mapper.Mapper的map方法根据请求路径匹配Context,这块的代码就不详细分析了,感兴趣的同志可以自己去看看源码。

接着调用connector.getService().getContainer().getPipeline().getFirst().invoke(request, response)方法,经过调用来到StandardHostValve的invoke方法,该方法源码如下:

复制代码
public void invoke(Request request, Response response) throws IOException, ServletException {

        Context context = request.getContext();
        try {
            context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
            try {
                if (!response.isErrorReportRequired()) {
                    context.getPipeline().getFirst().invoke(request, response);
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                container.getLogger().error(sm.getString("standardHostValve.exception", request.getRequestURI()), t);
            }
            response.setSuspended(false);

            Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
            
            if (!context.getState().isAvailable()) {
                return;
            }
            if (response.isErrorReportRequired()) {
                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 {
            if (ACCESS_SESSION) {
                request.getSession(false);
            }

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

该方法主要是获取到请求路径解析出来的StandardContext对象,调用bind方法,这个方法我已经在启动时候分析过了它主要是切换当前的ClassLoader为ParallelWebappClassLoader,这样做的目的是为了加载指定目录下的Filter、Servlet等。 接着调用context.getPipeline().getFirst().invoke(request, response)方法,该方法会最后执行到StandardContextValve类的invoke方法,该方法源码如下:

复制代码
public void invoke(Request request, Response response) throws IOException, ServletException {
    Wrapper wrapper = request.getWrapper();
    if (wrapper == null || wrapper.isUnavailable()) {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    }
    wrapper.getPipeline().getFirst().invoke(request, response);
}

还是从请求解析获取出Wrapper对象,它里面包含Servlet对象,判断如果wrapper为空返回错误,接着调用wrapper.getPipeline().getFirst().invoke(request, response),这时候请求来到类StandardWrapperValve的invoke方法,这里是真正执行Filter、Servlet实现逻辑的地方,下面我们详细看一下它的源码实现:

复制代码
public void invoke(Request request, Response response) throws IOException, ServletException {
        boolean unavailable = false;
        Throwable throwable = null;
        long t1 = System.currentTimeMillis();
        requestCount.incrementAndGet();
        StandardWrapper wrapper = (StandardWrapper) getContainer();
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();
        try {
            if (!unavailable) {
                servlet = wrapper.allocate();
            }
        } catch (UnavailableException e) {
            container.getLogger().error(sm.getString("standardWrapper.allocateException", wrapper.getName()), e);
            checkWrapperAvailable(response, wrapper);
        } catch (ServletException e) {
            container.getLogger().error(sm.getString("standardWrapper.allocateException", wrapper.getName()),
                    StandardWrapper.getRootCause(e));
            throwable = e;
            exception(request, response, e);
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            container.getLogger().error(sm.getString("standardWrapper.allocateException", wrapper.getName()), t);
            throwable = t;
            exception(request, response, t);
        }
        ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
        Container container = this.container;
        try {
            if ((servlet != null) && (filterChain != null)) {
                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.isEmpty()) {
                            context.getLogger().info(log);
                        }
                    }
                } else {
                    if (request.isAsyncDispatching()) {
                        request.getAsyncContextInternal().doInternalDispatch();
                    } else {
                        filterChain.doFilter(request.getRequest(), response.getResponse());
                    }
                }

            }
        } catch (BadRequestException e) {
            if (container.getLogger().isDebugEnabled()) {
                container.getLogger().debug(
                        sm.getString("standardWrapper.serviceException", wrapper.getName(), context.getName()), e);
            }
            throwable = e;
            exception(request, response, e, HttpServletResponse.SC_BAD_REQUEST);
        } catch (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 ioe) {
            container.getLogger()
                    .error(sm.getString("standardWrapper.serviceException", wrapper.getName(), context.getName()), ioe);
            throwable = ioe;
            exception(request, response, ioe);
        } catch (UnavailableException e) {
            container.getLogger()
                    .error(sm.getString("standardWrapper.serviceException", wrapper.getName(), context.getName()), e);
            wrapper.unavailable(e);
            checkWrapperAvailable(response, wrapper);
        } catch (ServletException e) {
            Throwable rootCause = StandardWrapper.getRootCause(e);
            if (!(rootCause instanceof BadRequestException)) {
                container.getLogger().error(sm.getString("standardWrapper.serviceExceptionRoot", wrapper.getName(),
                        context.getName(), e.getMessage()), rootCause);
            }
            throwable = e;
            exception(request, response, e);
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            container.getLogger()
                    .error(sm.getString("standardWrapper.serviceException", wrapper.getName(), context.getName()), t);
            throwable = t;
            exception(request, response, t);
        } finally {
            if (filterChain != null) {
                filterChain.release();
            }
            try {
                if (servlet != null) {
                    wrapper.deallocate(servlet);
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                container.getLogger().error(sm.getString("standardWrapper.deallocateException", wrapper.getName()), t);
                if (throwable == null) {
                    throwable = t;
                    exception(request, response, t);
                }
            }
            try {
                if ((servlet != null) && (wrapper.getAvailable() == Long.MAX_VALUE)) {
                    wrapper.unload();
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                container.getLogger().error(sm.getString("standardWrapper.unloadException", wrapper.getName()), t);
                if (throwable == null) {
                    exception(request, response, t);
                }
            }
        }
}

获取Wrapper调用它的allocate方法,这个方法逻辑主要是判断它里面的成员变量Servlet是否为空,为空说明还没有实例化该Servlet对象,会用ParallelWebappClassLoader类加载器实例化对象并调用它的init方法。

接着执行这段代码ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet),它的方法如下:

复制代码
public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {

        if (servlet == null) {
            return null;
        }
        ApplicationFilterChain filterChain;
        if (request instanceof Request) {
            Request req = (Request) request;
            if (Globals.IS_SECURITY_ENABLED) {
                filterChain = new ApplicationFilterChain();
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            filterChain = new ApplicationFilterChain();
        }

        filterChain.setServlet(servlet);
        filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
        StandardContext context = (StandardContext) wrapper.getParent();
        FilterMap filterMaps[] = context.findFilterMaps();
        if (filterMaps == null || filterMaps.length == 0) {
            return filterChain;
        }

        DispatcherType dispatcher = (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

        String requestPath = FilterUtil.getRequestPath(request);

        String servletName = wrapper.getName();

        for (FilterMap filterMap : filterMaps) {
            if (!matchDispatcher(filterMap, dispatcher)) {
                continue;
            }
            if (!FilterUtil.matchFiltersURL(filterMap, requestPath)) {
                continue;
            }
            ApplicationFilterConfig filterConfig =
                    (ApplicationFilterConfig) context.findFilterConfig(filterMap.getFilterName());
            if (filterConfig == null) {
                log.warn(sm.getString("applicationFilterFactory.noFilterConfig", filterMap.getFilterName()));
                continue;
            }
            filterChain.addFilter(filterConfig);
        }
        for (FilterMap filterMap : filterMaps) {
            if (!matchDispatcher(filterMap, dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMap, servletName)) {
                continue;
            }
            ApplicationFilterConfig filterConfig =
                    (ApplicationFilterConfig) context.findFilterConfig(filterMap.getFilterName());
            if (filterConfig == null) {
                log.warn(sm.getString("applicationFilterFactory.noFilterConfig", filterMap.getFilterName()));
                continue;
            }
            filterChain.addFilter(filterConfig);
        }
        return filterChain;
 }

创建一个ApplicationFilterChain,它实现了Servlet的规范接口FilterChain,它主要是包含了Servlet和一些Filter的执行数组,并把创建好的对象设置到HttpServletRequest对象的成员变量FilterChain filterChain上,设置ApplicationFilterChain成员属性为匹配的Servelt,然后获取StandardContext中保存的FilterMap数组,它里面包含所有的Filter对象和url路径,如果FilterMap数组为空直接返回ApplicationFilterChain对象,否则根据当前的请求路径和FilterMap里面的每个url路径做匹配,如果匹配成功得到ApplicationFilterConfig对象,调用filterChain.addFilter(filterConfig),把ApplicationFilterConfig对象放在ApplicationFilterChain成员属性ApplicationFilterConfig[]上。

接着又来遍历一次FilterMap找出和servletName名称匹配的Filter,如果是匹配所有返回true,如果不是就根据Filter里面配置的Servlet名称和当前Servlet名称做匹配如果有同样加入到ApplicationFilterChain成员属性ApplicationFilterConfig[]上。

得到了ApplicationFilterChain对象以后就调用了它的doFilter方法,传入HttpServletRequest对象和HttpServletResponse对象,内部又调用了internalDoFilter方法,该方法源码如下:

复制代码
private void internalDoFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException {

        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            try {
                Filter filter = filterConfig.getFilter();

                if (request.isAsyncSupported() && !(filterConfig.getFilterDef().getAsyncSupportedBoolean())) {
                    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);
                }
            } catch (IOException | ServletException | RuntimeException e) {
                throw e;
            } catch (Throwable t) {
                t = ExceptionUtils.unwrapInvocationTargetException(t);
                ExceptionUtils.handleThrowable(t);
                throw new ServletException(sm.getString("filterChain.filter"), t);
            }
            return;
        }
 }       

获取ApplicationFilterConfig对象中的Filter对象然后调用它的doFilter方法,参数除了包含HttpServletRequest对象和HttpServletResponse对象,还包含了当前调用方法的对象即ApplicationFilterChain,以便下一个Filter可以继续调用其它的Filter对象。

Filter都执行完成以后调用servlet.service(request, response);进而执行Servlet的doGet或者doPost方法,真正执行我们的业务逻辑。

执行完以后按照调用链路一路返回到StandardHostValve类的invoke方法的finally代码块执行context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER)切换为Tomcat所使用的Classloader

三、总结

通过以上源码分析,我们可以清晰地看到Tomcat处理HTTP请求的完整路径:

请求流转路径: Connector → CoyoteAdapter → StandardHostValve → StandardContextValve → StandardWrapperValve → ApplicationFilterChain → Servlet

关键设计要点:

  • 分层架构:Tomcat采用典型的责任链模式,通过Pipeline-Valve机制实现请求的逐层传递

  • 请求封装:将底层org.apache.coyote.Request封装为Servlet规范的HttpServletRequest

  • 类加载隔离:通过context.bind()/context.unbind()实现Web应用类加载器的切换

  • Filter链构建:运行时动态构建匹配请求的Filter链,支持URL模式和Servlet名称两种匹配方式

  • Servlet生命周期:Wrapper负责Servlet的延迟初始化、缓存和销毁

相关推荐
To Be Clean Coder8 分钟前
【Spring源码】createBean如何寻找构造器(四)——类型转换与匹配权重
java·后端·spring
-孤存-19 分钟前
SpringBoot核心注解与配置详解
java·spring boot·后端
Hx_Ma1634 分钟前
BCrypt
java
We....35 分钟前
鸿蒙与Java跨平台Socket通信实战
java·服务器·tcp/ip·arkts·鸿蒙
笃行客从不躺平37 分钟前
Token 复习
java·分布式·spring cloud
Albert Edison1 小时前
【Python】函数
java·linux·python·pip
2301_818732061 小时前
项目启动报错,错误指向xml 已解决
xml·java·数据库·后端·springboot
码农阿豪2 小时前
Oracle 到金仓数据库迁移实战:一次真正“落地”的国产替代之旅
java·数据库·oracle
小王不爱笑1322 小时前
SpringBoot 整合 Ollama + 本地 DeepSeek 模型
java·spring boot·后端
毕设源码-钟学长2 小时前
【开题答辩全过程】以 高校宿舍分配系统设计与实现为例,包含答辩的问题和答案
java