Spring中WebMvc的请求处理流程

从Servlet容器到我们编写的handler方法,Spring帮我们做了很多事情。比如根据请求URL查找到目标handler方法,参数的类型转换及绑定,调用handler方法,以及返回值写回响应等等操作。本文就来分析一下这个过程中Spring到底帮我们做了哪些事情,以及是怎么做的。


Spring MVC使用的是单servlet模式,这个servlet就是DispatcherServlet,所有请求都会进入该对象中被处理。那servlet容器是怎么调用到DispatcherServlet的呢?先来搞清楚DipatcherServlet的继承体系。

DispatcherServlet的继承体系

Servlet规范定义当请求到来时,servlet中的service方法会被调用。

v8.5.59
Servlet
GenericServlet
HttpServlet
<
>
java
java/javax/servlet/Servlet.java
public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException;
java
java/javax/servlet/GenericServlet.java
@Override
public abstract void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException;
java
java/javax/servlet/http/HttpServlet.java
@Override
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException {

    HttpServletRequest  request;
    HttpServletResponse response;

    try {
        // 对请求响应对象进行转换
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;
    } catch (ClassCastException e) {
        throw new ServletException(lStrings.getString("http.non_http"));
    }
    service(request, response);
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {

    // 获取请求的方法类型
    String method = req.getMethod();

    /*
     * 下面根据不同类型的请求做不同的处理
     * 在Spring中,这里调用的doXXX方法在FrameworkServlet中都重写了。
     */

    if (method.equals(METHOD_GET)) { // 处理GET请求
        long lastModified = getLastModified(req);
        // 默认实现就是-1,所以种类满足条件
        if (lastModified == -1) {
            // servlet doesn't support if-modified-since, no reason
            // to go through further expensive logic
            // 处理请求
            doGet(req, resp);
        } else {
        }

    } else if (method.equals(METHOD_HEAD)) { // 处理HEAD请求
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        // 处理请求
        doHead(req, resp);

    } else if (method.equals(METHOD_POST)) { // 处理POST请求
        doPost(req, resp);

    } else if (method.equals(METHOD_PUT)) { // 处理PUT请求
        doPut(req, resp);

    } else if (method.equals(METHOD_DELETE)) { // 处理DELETE请求
        doDelete(req, resp);

    } else if (method.equals(METHOD_OPTIONS)) { // 处理OPTIONS请求
        doOptions(req,resp);

    } else if (method.equals(METHOD_TRACE)) { // 处理TRACE请求
        doTrace(req,resp);

    } else { // 不支持的请求方法类型
        //
        // Note that this means NO servlet supports whatever
        // method was requested, anywhere on this server.
        //

        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);

        // 响应错误
        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}

主要的实现在HttpServlet中,自己定义了重载的service方法,该方法中针对不同的HTTP请求方法,调用了不同函数处理。在Spring Framework中的FrameworkServlet重写了这个重载的service方法,目的是为了提供对PATCH类型请求的支持。

注意这里说的重载service方法,是相对于实现Servlet接口中的那个service方法而言的。

java
spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
    // 对PATCH请求的支持
    if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
        processRequest(request, response);
    }
    else {
        // 调用HttpServlet中的是实现
        super.service(request, response);
    }
}

可以看到,大部分请求还是通过父类HttpServlet的重载service方法来实现的。但是调用的doXXX方法在FrameworkServlet中都被重写了,所以这里直接看这个子类中的即可。

java
spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    processRequest(request, response);
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {


    try {
        // 请求处理,这是个模板方法,由子类实现
        doService(request, response);
    }
}
// 模板方法,由子类实现
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
        throws Exception;

这里只给出了doGetdoPost的实现,对于其他类型请求的doXXX方法是相似的,都是调用的processRequest方法,而在该方法中,调用了子类实现的doService方法,下面就来看一下DispatcherServlet中是怎么实现的。

java
spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

    RequestPath previousRequestPath = null;
    // 这个属性是在initHandlerMappings方法中被设置的,在Spring Boot中默认是true
    if (this.parseRequestPath) {
        // 获取当前Request对象承载的上一次请求的路径
        previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
        // 解析当前请求的路径
        ServletRequestPathUtils.parseAndCache(request);
    }

    try {
        // 处理请求
        doDispatch(request, response);
    }
}
@SuppressWarnings("deprecation")
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            // 检查请求是不是包含了文件上传
            processedRequest = checkMultipart(request);
            // 设置是否是文件上传请求
            multipartRequestParsed = (processedRequest != request);

            // Determine handler for the current request.
            // 获取封装了handler和interceptor的执行链
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                // 处理没有找到handler的情况
                noHandlerFound(processedRequest, response);
                return;
            }

            // Determine handler adapter for the current request.
            // 获取HandlerAdapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());


            // 执行拦截器前置逻辑,如果有一个拦截器,返回false,那么后续逻辑不会执行了。
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // Actually invoke the handler.
            // 调用handler方法
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            // 应用默认视图名称,这一步可以忽略,因为很多项目都是前后段分离的,不会渲染视图,所以这里不会用到。
            applyDefaultViewName(processedRequest, mv);

            // 执行拦截器后置逻辑
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }

        // 处理执行结果
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    finally {
        else {
            // Clean up any resources used by a multipart request.
            // 如果是文件上传请求
            if (multipartRequestParsed) {
                // 清理上传的文件
                cleanupMultipart(processedRequest);
            }
        }
    }
}

doService方法中,忽略掉非主要的实现,主要就是调用的doDispatch方法来处理请求。在doDispatch方法中,包含了Spring MVC的核心实现,下面依次来分析该方法中的执行过程。

处理文件上传

java
spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
    if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
        else {
            try {
                // 解析文件
                return this.multipartResolver.resolveMultipart(request);
            }
        }
    }
    // If not returned before: return original request.
    return request;
}

该方法中主要做了两件事情:

  • 判断请求中是否上传了文件;
  • 处理请求中的文件;

::: 由于不同的MultipartResolver有不同的实现,在Spring Boot中默认配置的是StandardServletMultipartResolver,所以以该类为例进行分析。 :::

isMultipart

isMultipart
MULTIPART_FORM_DATA_VALUE
<
>
java
spring-web/src/main/java/org/springframework/web/multipart/support/StandardServletMultipartResolver.java
private boolean resolveLazily = false;

private boolean strictServletCompliance = false;
@Override
public boolean isMultipart(HttpServletRequest request) {
    /*
     * 从Content-Type中查找是否以multipart关键字开头。
     */
    return StringUtils.startsWithIgnoreCase(request.getContentType(),
            // strictServletCompliance默认是false,表示不用严格遵守servlet的规范,所以只要contentType以“multipart/”开头就行了。
            (this.strictServletCompliance ? MediaType.MULTIPART_FORM_DATA_VALUE : "multipart/"));
}
java
spring-web/src/main/java/org/springframework/http/MediaType.java
public static final String MULTIPART_FORM_DATA_VALUE = "multipart/form-data";

可以看到,这里是在判断请求头中的ContentType,默认情况下,只要以"multipart/"开头就会被认为是文件上传请求。

resolveMultipart

java
spring-web/src/main/java/org/springframework/web/multipart/support/StandardServletMultipartResolver.java
private boolean resolveLazily = false;

private boolean strictServletCompliance = false;
	@Override
	public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
		/*
		 * 创建standard multipart请求对象
		 * 如果不是延迟解析,那么会在其构造器中就执行解析
		 */
		return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
	}

请求的解析在StandardMultipartHttpServletRequest的构造方法中。

java
spring-web/src/main/java/org/springframework/web/multipart/support/StandardMultipartHttpServletRequest.java
public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing)
        throws MultipartException {

    super(request);
    if (!lazyParsing) {
        // 解析请求
        parseRequest(request);
    }
}
private void parseRequest(HttpServletRequest request) {
    try {
        // getParts由具体的servlet容器实现
        Collection<Part> parts = request.getParts();
        // 保存参数名称
        this.multipartParameterNames = new LinkedHashSet<>(parts.size());
        // 该集合保存了上传的多个文件
        MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size());
        // 遍历多个文件并处理
        for (Part part : parts) {
            // 获取Content-Disposition这个请求头字段
            String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION);
            // 解析Content-Disposition这个请求头
            ContentDisposition disposition = ContentDisposition.parse(headerValue);
            // 获取文件名称
            String filename = disposition.getFilename();
            if (filename != null) { // 如果是文件
                // 对RFC 2047 (MIME encoding for non-ASCII text in headers)的支持
                if (filename.startsWith("=?") && filename.endsWith("?=")) {
                    // 对文件名称进行解码
                    filename = MimeDelegate.decode(filename);
                }
                // 创建MultipartFile对象,文件数据在part对象中
                files.add(part.getName(), new StandardMultipartFile(part, filename));
            }
            else { // 如果是普通参数
                this.multipartParameterNames.add(part.getName());
            }
        }
        // 将文件对象列表放入请求中
        setMultipartFiles(files);
    }
    catch (Throwable ex) {
        handleParseFailure(ex);
    }
}

这里是在解析请求中的参数,将文件和普通参数分开存储。这里的文件对象类型是StandardMultipartFile,该对象最终会被传递给handler方法的MultipartFile参数。

获取handler

java
spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        // 遍历列表中的多个handlerMapping,越前面的优先级越高
        for (HandlerMapping mapping : this.handlerMappings) {
            // 获取执行链,主要都是通过RequestMappingHandlerMapping来获取的
            HandlerExecutionChain handler = mapping.getHandler(request);
            // 如果找到了,就返回
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

这里以RequestMappingHandlerMapping为例进行分析,主要的实现在AbstractHandlerMethodMapping中。

java
spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMapping.java
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 调用子类的实现,具体参考RequestMappingHandlerMapping
    Object handler = getHandlerInternal(request);
    if (handler == null) {
        // 获取默认的handler
        handler = getDefaultHandler();
    }
    // 如果没找到handler
    if (handler == null) {
        // 则返回null,让调用方从其他的HandlerMapping中查找看看能不能找到合适的handler
        return null;
    }
    // Bean name or resolved handler?
    // 如果handler是个字符串
    if (handler instanceof String) {
        String handlerName = (String) handler;
        // 将handler的名称作为bean的名称从bean工厂中获取bean
        handler = obtainApplicationContext().getBean(handlerName);
    }

    // Ensure presence of cached lookupPath for interceptors and others
    // 如果查询缓存还没有记录,
    if (!ServletRequestPathUtils.hasCachedPath(request)) {
        // 则初始化缓存项
        initLookupPath(request);
    }

    // 获取执行链
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);


    // 跨域相关
    if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
        CorsConfiguration config = getCorsConfiguration(handler, request);
        if (getCorsConfigurationSource() != null) {
            CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
            config = (globalConfig != null ? globalConfig.combine(config) : config);
        }
        if (config != null) {
            config.validateAllowCredentials();
        }
        executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }

    return executionChain;
}

该方法主要做了两件事情:

  • 获取handler方法;
  • 将handler方法封装为执行链;

获取handler方法

AbstractHandlerMapping中的getHandlerInternal方法是个模板方法,子类RequestMappingInfoHandlerMapping中实现了,但是主要是调用父类AbstractHanlderMethodMapping中的实现。这几个类的的关系文字叙述起来很绕,直接看下面的类图。

RequestMappingInfoHandlerMapping
AbstractHandlerMethodMapping
<
>
java
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java
@Override
@Nullable
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
    try {
        // 获取handler
        return super.getHandlerInternal(request);
    }
    finally {
        ProducesRequestCondition.clearMediaTypesAttribute(request);
    }
}
java
spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java
@Override
@Nullable
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 获取查找路径
    String lookupPath = initLookupPath(request);
    this.mappingRegistry.acquireReadLock();
    try {
        // 查找handler方法
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
        this.mappingRegistry.releaseReadLock();
    }
}

主要来参考AbstractHandlerMethodMapping中的实现。

获取查找路径

java
spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMapping.java
protected String initLookupPath(HttpServletRequest request) {
    // 是否使用路径模式,在Spring Boot中默认情况下满足条件
    if (usesPathPatterns()) {
        request.removeAttribute(UrlPathHelper.PATH_ATTRIBUTE);
        // 解析请求路径
        RequestPath requestPath = ServletRequestPathUtils.getParsedRequestPath(request);
        // 获取请求路径的值
        String lookupPath = requestPath.pathWithinApplication().value();
        // 移除路径中的分号
        return UrlPathHelper.defaultInstance.removeSemicolonContent(lookupPath);
    }
    else {
        // 解析并缓存查找路径
        return getUrlPathHelper().resolveAndCacheLookupPath(request);
    }
}

ServletRequestPathUtilsgetParsedRequestPath方法中还是有些许复杂的,但现在只需要直到这里获取到了请求路径就行了。

查找handler方法

下面来看这个获取handler操作的核心,是如何查找handler方法。

java
spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<>();
    // 根据路径查找RequestMappingInfo
    List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
    // 如果能直接匹配上
    if (directPathMatches != null) {
        // 将结果添加到matches列表中
        addMatchingMappings(directPathMatches, matches, request);
    }
    // 如果没有匹配上的handler
    if (matches.isEmpty()) {
        addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
    }
    // 如果存在匹配上了的handler
    if (!matches.isEmpty()) {
        Match bestMatch = matches.get(0);
        // 如果有多个匹配
        if (matches.size() > 1) {
            // 创建比较器
            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
            // 排序
            matches.sort(comparator);
            // 默认获取第一个作为最佳匹配
            bestMatch = matches.get(0);
            if (logger.isTraceEnabled()) {
                logger.trace(matches.size() + " matching mappings: " + matches);
            }
            // 是否是预检请求
            if (CorsUtils.isPreFlightRequest(request)) {
                for (Match match : matches) {
                    if (match.hasCorsConfig()) {
                        // 返回特殊的HandlerMethod
                        return PREFLIGHT_AMBIGUOUS_MATCH;
                    }
                }
            }
            else {
                // 获取次最佳匹配
                Match secondBestMatch = matches.get(1);
                // 比较最佳和次最佳的两个handler方法是否是同等级别的,虽然上面排了序,但是可能两个同等级别的排在最前面
                if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                    Method m1 = bestMatch.getHandlerMethod().getMethod();
                    Method m2 = secondBestMatch.getHandlerMethod().getMethod();
                    String uri = request.getRequestURI();
                    // 如果是同等级别的则会报错
                    throw new IllegalStateException(
                            "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
                }
            }
        }
        // 把最佳handler添加到请求属性中
        request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
        handleMatch(bestMatch.mapping, lookupPath, request);
        // 获取handler方法
        return bestMatch.getHandlerMethod();
    }
    else {
        // 没有匹配上
        return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
    }
}

封装执行链

java
spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMapping.java
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
            (HandlerExecutionChain) handler :
            // 创建执行链对象
            new HandlerExecutionChain(handler));

    // 遍历拦截器
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
        if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            // 是否能拦截该请求
            if (mappedInterceptor.matches(request)) {
                // 如果匹配请求则添加到执行链中
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }
        else {
            chain.addInterceptor(interceptor);
        }
    }
    return chain;
}

这里遍历的拦截器是什么时候设置的呢?AbstractHandlerMapping继承了ApplicationObjectSupport,而后者又实现了ApplicationContextAware接口并实现了setApplicationContext方法。在该方法中最终会调用到AbstractHandlerMapping中的initApplicationContext方法。

java
spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMapping.java
@Override
protected void initApplicationContext() throws BeansException {
    // 扩展拦截器,默认是空实现
    extendInterceptors(this.interceptors);
    // 获取拦截器
    detectMappedInterceptors(this.adaptedInterceptors);
    // 初始化拦截器
    initInterceptors();
}
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
    // 获取bean工厂中所有MappedInterceptor类型的bean
    mappedInterceptors.addAll(BeanFactoryUtils.beansOfTypeIncludingAncestors(
            obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
protected void initInterceptors() {
    if (!this.interceptors.isEmpty()) {
        // 遍历拦截器对象
        for (int i = 0; i < this.interceptors.size(); i++) {
            Object interceptor = this.interceptors.get(i);
            // 确保列表中的拦截器对象不能是null
            if (interceptor == null) {
                throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
            }
            // 将拦截器转换为HandlerInterceptor类型的对象
            this.adaptedInterceptors.add(adaptInterceptor(interceptor));
        }
    }
}

获取handler适配器

上面获取到handler后,就是根据handler来获取handler适配器。

getHandlerAdapter
supports
supportsInternal
<
>
java
spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        // 遍历适配器列表
        for (HandlerAdapter adapter : this.handlerAdapters) {
            // 判断适配器是否支持该handler
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    }
    // 如果没有支持当前handler的适配器,则抛出异常
    throw new ServletException("No adapter for handler [" + handler +
            "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
java
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/AbstractHandlerMethodAdapter.java
@Override
public final boolean supports(Object handler) {
    return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
java
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
    // 支持所有请求
    return true;
}

对于常用的HandlerMethod而言,使用的是RequestMappingHandlerAdapter这个适配器,所以本文而以该类为例介绍。

调用拦截器前置逻辑

接下来是执行拦截器的前置逻辑。

java
spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerExecutionChain.java
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 遍历拦截器列表
    for (int i = 0; i < this.interceptorList.size(); i++) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        // 调用拦截器的前置逻辑,即preHandle方法
        if (!interceptor.preHandle(request, response, this.handler)) {
            triggerAfterCompletion(request, response, null);
            return false;
        }
        this.interceptorIndex = i;
    }
    return true;
}

如果有一个拦截器,返回false,那么后续逻辑不会执行了。

执行handler方法

接下来是最重要的步骤,执行handler方法,即真正的处理请求。

java
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/AbstractHandlerMethodAdapter.java
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {

    return handleInternal(request, response, (HandlerMethod) handler);
}
@Nullable
protected abstract ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;

核心实现在子类RequestMappingHandlerAdapter中。

java
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ModelAndView mav;
    checkRequest(request);

    // Execute invokeHandlerMethod in synchronized block if required.
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // No HttpSession available -> no mutex necessary
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    }
    else {
        // No synchronization on session demanded at all...
        // 调用handler方法
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }

    // 如果响应头中不包含Cache-Control
    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
        if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
            applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
        }
        else {
            prepareResponse(response);
        }
    }

    return mav;
}

上面方法中的几种情况都会调用到invokeHandlerMethod方法。

java
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response); // 封装请求响应对象
    try {
        // 获取数据绑定工厂对象
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        // 获取模型工厂
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

        // 真正对handlerMethod的invoke操作其实是委托给invocableMethod来实现的,所以invocableMethod是关键核心
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        if (this.argumentResolvers != null) {
            /*
             * 设置参数解析器
             * argumentResolvers是在afterPropertiesSet方法中被调用的。
             * 下面设置的returnValueHandlers也一样。
             */
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            // 设置返回值处理器
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        // 设置binder工厂
        invocableMethod.setDataBinderFactory(binderFactory);
        // 设置参数名称发现器
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

        // 创建模型视图容器
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

        // 异步支持
        AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.setTaskExecutor(this.taskExecutor);
        asyncManager.setAsyncWebRequest(asyncWebRequest);
        asyncManager.registerCallableInterceptors(this.callableInterceptors);
        asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

        if (asyncManager.hasConcurrentResult()) {
            Object result = asyncManager.getConcurrentResult();
            mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();
            LogFormatUtils.traceDebug(logger, traceOn -> {
                String formatted = LogFormatUtils.formatValue(result, !traceOn);
                return "Resume with async result [" + formatted + "]";
            });
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }

        // 调用方法并处理返回值
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }

        // 内部会模型视图对象
        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
        webRequest.requestCompleted();
    }
}

创建可执行的handler方法

java
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
    return new ServletInvocableHandlerMethod(handlerMethod);
}

这一步很简单,将handler方法封装到ServletInvocableHandlerMethod对象中,以便后续调用。 然后为ServletInvocableHandlerMethod对象设置了参数和返回值解析器,分别用于后续的参数解析和返回值处理。

执行handler方法

java
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    // 调用方法
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    // 处理@ResponseStatus注解
    setResponseStatus(webRequest);

    // 如果没有返回值
    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            // 丢弃缓存
            disableContentCachingIfNecessary(webRequest);
            mavContainer.setRequestHandled(true);
            return;
        }
    }
    // 如果存在@ResponseStatus注解,且reason字段不是空字符串
    else if (StringUtils.hasText(getResponseStatusReason())) {
        mavContainer.setRequestHandled(true);
        return;
    }

    mavContainer.setRequestHandled(false);
    // 执行到这里是需要返回值处理器处理的情况,所以returnValueHandlers不能是null。
    Assert.state(this.returnValueHandlers != null, "No return value handlers");
    try {
        // 通过返回值处理器来处理返回值
        this.returnValueHandlers.handleReturnValue(
                returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
    catch (Exception ex) {
        if (logger.isTraceEnabled()) {
            logger.trace(formatErrorForReturnValue(returnValue), ex);
        }
        throw ex;
    }
}

方法真正的执行是在父类InvocableHandlerMethod中的invokeForRequest方法中。

java
spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    // 获取方法参数
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }
    // 执行方法
    return doInvoke(args);
}

上面两个方法的逻辑可以分为下面三步:

  • 获取参数;
  • 执行方法;
  • 处理返回值;

获取参数

getMethodArgumentValues
findProvidedArgument
<
>
java
spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    MethodParameter[] parameters = getMethodParameters(); // 获取方法参数,将每个参数封装成MethodParameter对象
    if (ObjectUtils.isEmpty(parameters)) {
        return EMPTY_ARGS;
    }

    // 保存参数实参的数组
    Object[] args = new Object[parameters.length];
    // 遍历形参
    for (int i = 0; i < parameters.length; i++) {
        MethodParameter parameter = parameters[i];
        // 设置参数名称发现器
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        // 判断是否提供了该参数,在RequestMappingHandlerAdapter的invokeHandlerMethod方法中调用过来时,没有传递的providedArgs参数
        args[i] = findProvidedArgument(parameter, providedArgs);
        // 对于大多数正常情况下,args[i]肯定是null。因为大部分都是从请求中获取参数。
        if (args[i] != null) {
            continue;
        }
        /*
         * 判断参数解析器集合中是否存在能够解析该参数的解析器,
         * 如果没有一个解析器支持解析该参数,那么抛出异常
         */
        if (!this.resolvers.supportsParameter(parameter)) {
            throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
        }
        try {
            // 从请求中解析参数
            args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
        }
    }
    return args;
}
java
spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java
@Nullable
protected static Object findProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) {
    if (!ObjectUtils.isEmpty(providedArgs)) {
        // 遍历提供的实际参数
        for (Object providedArg : providedArgs) {
            // 如果实参类型能够满足形参类型
            if (parameter.getParameterType().isInstance(providedArg)) {
                // 则返回参数值
                return providedArg;
            }
        }
    }
    return null;
}

这里会先判断是否有支持该参数的参数解析器,如果没有则会抛出异常

java
spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java
@Override
public boolean supportsParameter(MethodParameter parameter) {
    return getArgumentResolver(parameter) != null;
}
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    // 从缓存中获取
    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    if (result == null) {
        // 遍历参数解析器
        for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
            // 是否支持当前参数
            if (resolver.supportsParameter(parameter)) {
                result = resolver;
                // 如果支持则放入缓存中,下次就不用再遍历了
                this.argumentResolverCache.put(parameter, result);
                break;
            }
        }
    }
    return result;
}

然后通过参数解析器来从请求中或者模型视图容器中解析参数。

java
spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

    /*
     * 根据参数寻找合适的解析器
     * 这里需要去看一下Spring提供的默认的参数解析器都能够解析哪些参数,也就是去看各种HandlerMethodArgumentResolver的supportsParameter方法
     */
    HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
    if (resolver == null) {
        throw new IllegalArgumentException("Unsupported parameter type [" +
                parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
    }
    // 使用解析器解析参数
    return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

由于Spring中的参数解析器太多,这里就不一一例举了,后面单独写文章来分析一些常用的参数解析器。

执行方法

java
spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java
@Nullable
protected Object doInvoke(Object... args) throws Exception {
    Method method = getBridgedMethod();
    try {
        // 通过反射调用我们编写的handler方法
        return method.invoke(getBean(), args);
    }
}

这里通过反射调用我们编写的handler方法,至此请求入站的Spring框架部分分析完毕,下面属于出站部分。

处理返回值

ServletInvocableHandlerMethodinvokeAndHandle方法中,先是处理了@ResponseStatus注解,这个用得比较少,省略介绍;主要是通过返回值处理器来处理。

java
spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

    // 根据返回值选择处理器
    HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
    // 没有处理器能够处理返回值
    if (handler == null) {
        throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
    }
    // 通过具体的处理器来处理返回值
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
    // 判断是否是异步返回值
    boolean isAsyncValue = isAsyncReturnValue(value, returnType);
    for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
        // 如果处理值是处理异步的情况,则跳过
        if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
            continue;
        }
        // 判断处理器是否支持这种返回值类型
        if (handler.supportsReturnType(returnType)) {
            return handler;
        }
    }
    return null;
}

由于Spring中的返回值处理器太多,这里就不一一例举了,后面单独写文章来分析一些常用的返回值处理器。

关于ModelAndView,现如今大量项目都是前后段分离项目,后端只提供JSON响应,不渲染视图。所以本文不会分析ModelAndView。 实际上,大量项目的控制器类都被@ResponseBody或者@RestController注解修饰,返回值会被RequestResponseBodyMethodProcessor处理,该类会把ModelAndViewContainer中的requestHandled属性标记为true,所以后续回到RequestMappingHandlerAdaptergetModelAndView方法中时,是不会创建ModelAndView对象的。

handleReturnValue
getModelAndView
<
>
java
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

    // 设置设置为true,后续不会创建ModelAndView对象
    mavContainer.setRequestHandled(true);
    ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
    ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

    // Try even with null return value. ResponseBodyAdvice could get involved.
    // 将JSON数据写入响应体中
    writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
java
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
        ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

    // 更新模型
    modelFactory.updateModel(webRequest, mavContainer);
    // 如果请求已经处理过了,则返回null
    if (mavContainer.isRequestHandled()) {
        return null;
    }
    ModelMap model = mavContainer.getModel();
    // 创建mv对象
    ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
    if (!mavContainer.isViewReference()) {
        mav.setView((View) mavContainer.getView());
    }
    if (model instanceof RedirectAttributes) {
        Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        if (request != null) {
            RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
        }
    }
    return mav;
}

调用拦截器后置逻辑

java
spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerExecutionChain.java
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
        throws Exception {

    // 遍历拦截器
    for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        // 执行拦截器的后置逻辑,即postHandle方法
        interceptor.postHandle(request, response, this.handler, mv);
    }
}

文件清理

java
spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java
protected void cleanupMultipart(HttpServletRequest request) {
    if (this.multipartResolver != null) {
        MultipartHttpServletRequest multipartRequest =
                WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
        if (multipartRequest != null) {
            // 清理文件
            this.multipartResolver.cleanupMultipart(multipartRequest);
        }
    }
}

调用具体的MultipartResolver来实现,本文以StandardServletMultipartResolver为例。

java
spring-web/src/main/java/org/springframework/web/multipart/support/StandardServletMultipartResolver.java
@Override
public void cleanupMultipart(MultipartHttpServletRequest request) {
    if (!(request instanceof AbstractMultipartHttpServletRequest) ||
            ((AbstractMultipartHttpServletRequest) request).isResolved()) {
        // To be on the safe side: explicitly delete the parts,
        // but only actual file parts (for Resin compatibility)
        try {
            // 遍历每个文件
            for (Part part : request.getParts()) {
                if (request.getFile(part.getName()) != null) {
                    /*
                     * 这里要看具体servlet容器的实现
                     */
                    part.delete();
                }
            }
        }
        catch (Throwable ex) {
            LogFactory.getLog(getClass()).warn("Failed to perform cleanup of multipart items", ex);
        }
    }
}

注意,Spring在请求处理末尾会删除上传的文件,如果在业务中需要异步处理文件的,首先需要同步拷贝文件,否则其他线程读文件时,该文件已经被删除了。而且这种问题的偶发性的,有时出现有时不出现,原因就在于线程调度,取决于读线程是否在删除文件之前就读到内容。

总结

本文梳理了Spring MVC请求的处理过程,大致可以总结为下面5个步骤:

  • 获取执行链,包括handler方法和拦截器;
  • 获取handler适配器;
  • 执行拦截器的前置逻辑;
  • 执行handler方法;
  • 执行拦截器的后置逻辑;

如果要加上视图的话,还应有视图解析和视图渲染。