一文带你深入理解SpringMVC的执行原理

简介: 【7月更文挑战第1天】阿里云产品初体验SpringMVC的执行原理本文分析的问题:文件上传的请求的处理、跨域的处理、是怎么找到目标方法的、目标方法的执行、目标方法参数的封装、返回值的处理、拦截器的执行、中间出现异常时的处理涉及组件:文件上传解析器、跨域处理器、处理器映射器、处理器注册中心、处理器执行链、处理器适配器、参数解析器、数据绑定器、类型转换器、返回值处理器、内容协商管理器、消息转换器、异常解析器、

今天大致来看一下Spring MVC的执行流程是什么样的

执行流程:也就是一个请求是怎么到我们Controller的,返回值是怎么给客户端的

本文分析的问题

  1. 文件上传的请求是怎么处理的
  2. 跨域是怎么处理的
  3. 是怎么找到目标方法的
  4. 是怎么执行目标方法的
  5. 目标方法的参数是怎么封装的
  6. 返回值是怎么处理的
  7. 拦截器是怎么执行的
  8. 中间出现异常是怎么处理的

整个执行流程涉及到的组件

文件上传解析器(MultipartResolver)、处理器映射器(HandlerMapping)、

跨域处理器(CorsProcessor)、处理器执行链(HandlerExecutionChain)、

处理器适配器(HandlerAdapter)、参数解析器(HandlerMethodArgumentResolver)、

数据绑定器(WebDataBinder)、类型转换器(Converter)、

返回值处理器(HandlerMethodReturnValueHandler)、

内容协商管理器(ContentNegotiationManager)、消息转换器(HttpMessageConverter)、

拦截器(HandlerInterceptor)、视图解析器(ViewResolver)

创建项目

我们都知道 DispatcherServlet(前端控制器) 这样的一个类,是这个类来帮我们执行的,网上的很多图以这个类为核心来画的,那是怎么来到这个类的?(大多数文章并没有说)又是怎么帮我们调用各个组件来执行这个请求的?这些都是问题,我们直接来看源码,看完源码再来画图理解。

首先创建一个最简单的项目:直接使用 Spring Initializer 来帮我们快速创建出一个Web项目,地址使用阿里的(https://start.aliyun.com )依赖就选择一个 starter-web 就行,就最简单的项目。

配置一下跨域,后面要分析这个原理

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
   

    @Override
    public void addCorsMappings(CorsRegistry registry) {
   
        // 添加映射路径,可以是特定路径或所有"/**"
        registry.addMapping("/**")
                // 允许的源(*表示允许任何源)
                .allowedOrigins("*")
                // 允许的请求方法
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                // 是否允许请求头携带验证信息(cookies)
                .allowCredentials(true)
                // 允许的请求头
                .allowedHeaders("*")
                // 预检请求的有效期,单位为秒。设置成-1则永久生效,不会再次发起预检请求。
                .maxAge(3600);
    }
}

先来到我们创建的这个项目的这个类的 BasicController#hello() 方法,并在这个上面打一个断点,然后启动项目 向这个地址发送一个请求:

image.png

还记得我们上篇文章说的怎么看源码的事情吗?直接来看调用栈:

image.png

我们通过调用栈又可以发现:

  • 红色框里是我们自己的代码

  • 紫色框里就是执行流程相关的了,SpringMVC 的底层不就是Servlet 吗。Servlet 不就是我们 JavaWeb 学的东西吗?Servlet、Filter、Listener 三大组件。

  • 蓝色框里是一些 filter 过滤器

我们直接在这里(HttpServlet)打一个断点,然后发请求就再次来到了这个方法,然后来看流程:

HttpServlet

继承树

image.png

HttpServlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)

image.png

然后到 org.springframework.web.servlet.FrameworkServlet#service方法,

再到 HttpServlet#service(javax.servlet.http.HttpServletRequest,
javax.servlet.http.HttpServletResponse),我们来看一下这个方法:

    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
   

        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
   
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
   
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
   
                long ifModifiedSince;
                try {
   
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
   
                    // Invalid date header - proceed as if none was set
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
   
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
   
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
   
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
   
            doPost(req, resp);

        } else if (method.equals(METHOD_PUT)) {
   
            doPut(req, resp);

        } else if (method.equals(METHOD_DELETE)) {
   
            doDelete(req, resp);

        } else if (method.equals(METHOD_OPTIONS)) {
   
            doOptions(req,resp);

        } else if (method.equals(METHOD_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);
        }
    }

分析:就是根据不同的请求方式去执行。doGet、doPost、doPut、doDelete、doHead、doTrace。我们总共支持这么多种的请求方式

image.png

FrameworkServlet

我们这次的请求方式是 Get ,所以就来到了 FrameworkServlet#doGet 方法:

    @Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
   

        processRequest(request, response);
    }

这里面又调用了 org.springframework.web.servlet.FrameworkServlet#processRequest 方法:

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

        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;

        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        LocaleContext localeContext = buildLocaleContext(request);

        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

        initContextHolders(request, localeContext, requestAttributes);

        try {
   
            // 继续调用执行
            doService(request, response);
        }
        catch (ServletException | IOException ex) {
   
            failureCause = ex;
            throw ex;
        }
        catch (Throwable ex) {
   
            failureCause = ex;
            throw new NestedServletException("Request processing failed", ex);
        }

        finally {
   
            resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
   
                requestAttributes.requestCompleted();
            }
            logResult(request, response, failureCause, asyncManager);
            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

DispatcherServlet

这里面调用了 doService() 方法,这里的 doService() 方法是DispatcherServlet类的:

    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   
        logRequest(request);

        // Keep a snapshot of the request attributes in case of an include,
        // to be able to restore the original attributes after the include.
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
   
            attributesSnapshot = new HashMap<>();
            Enumeration<?> attrNames = request.getAttributeNames();
            while (attrNames.hasMoreElements()) {
   
                String attrName = (String) attrNames.nextElement();
                if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
   
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
        }

        // Make framework objects available to handlers and view objects.
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

        if (this.flashMapManager != null) {
   
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            if (inputFlashMap != null) {
   
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
            }
            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
        }

        try {
   
            // 核心方法
            doDispatch(request, response);
        }
        finally {
   
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
   
                // Restore the original attribute snapshot, in case of an include.
                if (attributesSnapshot != null) {
   
                    restoreAttributesAfterInclude(request, attributesSnapshot);
                }
            }
        }
    }

经过一层层的调用后,终于来到了最核心的方法 doDispatch()


    // 文件上传解析器
    /** MultipartResolver used by this servlet. */
    @Nullable
    private MultipartResolver multipartResolver;

    // 处理器映射器集合
    /** List of HandlerMappings used by this servlet. */
    @Nullable
    private List<HandlerMapping> handlerMappings;

    // 处理器适配器集合
    /** List of HandlerAdapters used by this servlet. */
    @Nullable
    private List<HandlerAdapter> handlerAdapters;

    // 视图解析器集合
    /** List of ViewResolvers used by this servlet. */
    @Nullable
    private List<ViewResolver> viewResolvers;

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);

            // 通过当前请求拿到对应的 HandlerExecutionChain 对象
            // 这一步是 HandlerMapping 
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
   
                noHandlerFound(processedRequest, response);
                return;
            }

            // 通过 HandlerExecutionChain 对象里的 Handler 拿到 HandlerAdapter 对象
            // 这一步是 HandlerAdapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // Process last-modified header, if supported by the handler.
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
   
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
   
                    return;
                }
            }

            // 拦截器的前置处理
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
   
                return;
            }

            // 通过 HandlerAdapter 来执行我们 Controller 里的方法
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

            applyDefaultViewName(processedRequest, mv);
            // 拦截器的后置处理
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
   
            dispatchException = ex;
        }
        catch (Throwable err) {
   
            // As of 4.3, we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        // 拦截器的最终处理 还会渲染 ModelAndView 对象
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
   
        // 异常时拦截器的最终处理
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
   
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                               new NestedServletException("Handler processing failed", err));
    }
    finally {
   
        if (asyncManager.isConcurrentHandlingStarted()) {
   
            // Instead of postHandle and afterCompletion
            if (mappedHandler != null) {
   
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
   
            // Clean up any resources used by a multipart request.
            if (multipartRequestParsed) {
   
                cleanupMultipart(processedRequest);
            }
        }
    }
}

流程:我们这里把几个核心的方法拿过来,然后一一做拆解

1,checkMultipart():处理文件上传的请求

2,getHandler(): 在这里 HandlerMapping 会根据当前请求拿到 HandlerExecutionChain 对象

3,getHandlerAdapter():拿到支持处理此 HandlerHandlerAdapter 对象

4,mappedHandler.applyPreHandle():拦截器的前置处理

5,ha.handle(): 目标方法的执行

6,mappedHandler.applyPostHandle(): 拦截器的后置处理

7,processDispatchResult(): 异常处理、渲染视图、拦截器的最终处理

8,上面任何一步出问题都会执行拦截器的最终处理

9,上面的那几个属性在启动的时候会初始化好,在初始化原理的时候再说

处理文件上传请求-checkMultipart()

涉及到的组件:文件上传解析器(MultipartResolver)

所有文件都会被封装为MultipartFile类,通过这个类我们可以很好的操作文件对象

protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
   
    // 1、文件解析器不为null并且是文件上传的请求
    if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
   
        // 是否通过MultipartFilter过滤器解析过
        if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
   
            if (DispatcherType.REQUEST.equals(request.getDispatcherType())) {
   
                logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
            }
        }
        else if (hasMultipartException(request)) {
   
            logger.debug("Multipart resolution previously failed for current request - " +
                         "skipping re-resolution for undisturbed error rendering");
        }
        else {
   
            try {
   
                // 2、通过文件上传解析器来截器文件上传的请求
                return this.multipartResolver.resolveMultipart(request);
            }
            catch (MultipartException ex) {
   
                if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
   
                    logger.debug("Multipart resolution failed for error dispatch", ex);
                    // Keep processing error dispatch with regular request handle below
                }
                else {
   
                    throw ex;
                }
            }
        }
    }
    // If not returned before: return original request.
    return request;
}

流程:

  1. 利用文件上传解析器来判断是否是文件上传的请求
  2. 通过文件上传解析器来截器文件上传的请求

1、利用文件上传解析器isMultipart()来判断是否是文件上传请求

其实就是判断Content-type的值是否是以multipart/multipart/form-data开头,这也就解释了为什么上传文件要选multipart/form-data类型

    public static final String MULTIPART_FORM_DATA_VALUE = "multipart/form-data";

    public boolean isMultipart(HttpServletRequest request) {
   
        return StringUtils.startsWithIgnoreCase(request.getContentType(),
                (this.strictServletCompliance ? MediaType.MULTIPART_FORM_DATA_VALUE : "multipart/"));
    }

2、使用文件上传解析器包装当前请求:resolveMultipart()

return new StandardMultipartHttpServletRequest(request, this.resolveLazily);

就是把当前请求包装为StandardMultipartHttpServletRequest类了

在这里还会把文件设置到MultiValueMap<String, MultipartFile> multipartFiles属性中,以便后续获取:

// 是否懒加载一样的解析
private boolean resolveLazily = false;

public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing)
    throws MultipartException {
   

    super(request);
    // 懒加载模式 默认不是懒加载模式
    if (!lazyParsing) {
   
        parseRequest(request);
    }
}

private void parseRequest(HttpServletRequest request) {
   
    try {
   
        // 获取到文件集合
        Collection<Part> parts = request.getParts();
        // 文件参数的名字
        this.multipartParameterNames = new LinkedHashSet<>(parts.size());
        // 封装好的文件集合
        MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size());
        for (Part part : parts) {
   
            String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION);
            ContentDisposition disposition = ContentDisposition.parse(headerValue);
            String filename = disposition.getFilename();
            if (filename != null) {
   
                // 添加到文件集合中 k:文件名  v:StandardMultipartFile对象
                files.add(part.getName(), new StandardMultipartFile(part, filename));
            }
            else {
   
                this.multipartParameterNames.add(part.getName());
            }
        }
        // 把上传的文件设置到目标属性中,以便后续封装参数时使用
        setMultipartFiles(files);
    }
    catch (Throwable ex) {
   
        handleParseFailure(ex);
    }
}

protected final void setMultipartFiles(MultiValueMap<String, MultipartFile> multipartFiles) {
   
    this.multipartFiles =
        new LinkedMultiValueMap<>(Collections.unmodifiableMap(multipartFiles));
}

最后在执行目标方法前一步会有参数解析器来封装参数,把上传的文件封装到方法参数上

获取处理器执行链对象-getHandler()

涉及到的组件:处理器映射器(HandlerMapping)、跨域处理器(CorsProcessor)、处理器执行链(HandlerExecutionChain)

  private List<HandlerMapping> handlerMappings;

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   
        if (this.handlerMappings != null) {
   
            for (HandlerMapping mapping : this.handlerMappings) {
   
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
   
                    return handler;
                }
            }
        }
        return null;
    }

分析:遍历所有的的HandlerMapping调用它的getHandler() 方法来拿到一个 HandlerExecutionChain 对象

就是这个五个 HandlerMapping对象,可自己查询这几个各自的作用

image.png

作用:就是根据路径找到对应的HandlerMethod(Handler)HandlerInterceptor、然后处理跨域,并封装为 HandlerExecutionChain 对象返回。

我们都是在controller接口中然后声明方法来做请求处理的,所以会由这个类处理 RequestMappingHandlerMapping(AbstractHandlerMapping 是它的父类):

private Object defaultHandler;

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   
    // 根据当前请求拿到对应的 Handler 
    Object handler = getHandlerInternal(request);
    // 没拿到就给一个默认的 Handler
    if (handler == null) {
   
        handler = getDefaultHandler();
    }
    if (handler == null) {
   
        return null;
    }
    // Bean name or resolved handler?
    if (handler instanceof String) {
   
        String handlerName = (String) handler;
        handler = obtainApplicationContext().getBean(handlerName);
    }

    // 根据拿到的 Handler 和 当前请求得到一个 HandlerExecutionChain
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

    if (logger.isTraceEnabled()) {
   
        logger.trace("Mapped to " + handler);
    }
    else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
   
        logger.debug("Mapped to " + executionChain.getHandler());
    }

    // 跨域处理
    if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
   
        CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
        CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
        config = (config != null ? config.combine(handlerConfig) : handlerConfig);
        executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }

    return executionChain;
}

获取处理器对象-getHandlerInternal()

最终会来到AbstractHandlerMethodMapping#getHandlerInternal

// 路径帮助器
private UrlPathHelper urlPathHelper = new UrlPathHelper();

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
   
    // 1、拿到请求中的请求路径
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    request.setAttribute(LOOKUP_PATH, lookupPath);
    this.mappingRegistry.acquireReadLock();
    try {
   
        // 2、通过请求路径拿到 HandlerMethod (核心)
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
   
        this.mappingRegistry.releaseReadLock();
    }
}

根据请求得到路径-getLookupPathForRequest()

调用底层api得到请求路径

public String getLookupPathForRequest(HttpServletRequest request) {
   
    String pathWithinApp = getPathWithinApplication(request);
    // Always use full path within current servlet context?
    if (this.alwaysUseFullPath) {
   
        return pathWithinApp;
    }
    // Else, use path within current servlet mapping if applicable
    String rest = getPathWithinServletMapping(request, pathWithinApp);
    if (StringUtils.hasLength(rest)) {
   
        return rest;
    }
    else {
   
        return pathWithinApp;
    }
}

image.png

根据路径找到对应处理器-lookupHandlerMethod()

根据请求路径找到HandlerMethod

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
   
    // 根据路径匹配到的集合
    List<Match> matches = new ArrayList<>();

    // 1,根据路径找到对应的 RequestMappingInfo 集合
    List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    if (directPathMatches != null) {
   
        // 2,匹配到的集合  这里面有 HandlerMethod 对象
        addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
   
        // No choice but to go through all mappings...
        addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
    }

    if (!matches.isEmpty()) {
   
        // 3,从匹配到的集合中拿到第一个
        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)) {
   
                return PREFLIGHT_AMBIGUOUS_MATCH;
            }
            Match secondBestMatch = matches.get(1);
            if (comparator.compare(bestMatch, secondBestMatch) == 0) {
   
                Method m1 = bestMatch.handlerMethod.getMethod();
                Method m2 = secondBestMatch.handlerMethod.getMethod();
                String uri = request.getRequestURI();
                throw new IllegalStateException(
                    "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
            }
        }
        request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);

        handleMatch(bestMatch.mapping, lookupPath, request);
        return bestMatch.handlerMethod;
    }
    else {
   
        return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
    }
}

总结:

  1. 去注册中心中根据路径找到对应的RequestMappingInfo
  2. 再根据找到的RequestMappingInfo来找HandlerMethod然后封装为Match对象,添加到matches集合中
  3. 拿到matches集合中的第一个元素(最佳匹配),然后通过handleMatch()方法来处理

1,调用getMappingsByUrl来得到对应的RequestMappingInfo集合:

image.png

注意

  1. PathVariable类型是不会加到这个集合里的,具体逻辑在这里,有兴趣可以去看这两个方法: org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register()、org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getDirectUrls()
  2. 这里的集合 key 是路径,value 是 RequestMappingInfo

2,得到结果后返回到 lookupHandlerMethod(),继续调用 addMatchingMappings() 方法,来找匹配到的 HandlerMethod:

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
   
    for (T mapping : mappings) {
   
        // 封装为 RequestMappingInfo
        T match = getMatchingMapping(mapping, request);
        if (match != null) {
   
            // 找到的集合,又会封装为 Match 对象
            matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
        }
    }
}

// 封装 RequestMappingInfo
return new RequestMappingInfo(this.name, pathPatterns, patterns,
      methods, params, headers, consumes, produces, custom, this.options);

这里的是:key是 RequestMappingInfo,value是 HandlerMethod

image.png

这就是我们找到的 Match 集合:

image.png

我们这里就找到了,然后返回到 lookupHandlerMethod() 方法来继续,会先拿到集合中的第一个元素,然后调用 handleMatch() 来处理路径问题,最后返回Match 对象中的 HandlerMethod。到这一步,我们终于拿到了想到的 HandlerMethod对象!

HandlerMethod

image.png

image.png

这个对象里面封装了我们的:对应的类、对应的方法、方法的参数类型、方法的返回值类型等等一系列信息

获取处理器执行链对象-getHandlerExecutionChain()

调用其中的 getHandlerExecutionChain() 方法来得到 HandlerExecutionChain 对象:

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
   
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                                   (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

    // 1、拿到当前路径
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
    // 2、遍历所有的拦截器
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
   
        if (interceptor instanceof MappedInterceptor) {
   
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            // 3、判断当前拦截器是否拦截此请求
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
   
                // 添加到目标对象中
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }
        else {
   
            chain.addInterceptor(interceptor);
        }
    }
    return chain;
}

流程:

  1. 先把 HandlerMethod 封装到 HandlerExecutionChain 中
  2. 拿到当前请求路径,再遍历所有的拦截器并判断当前拦截器是否拦截当前请求,拦截的话就添加到我们的 HandlerExecutionChain 对象中,然后返回

HandlerExecutionChain对象:

image.png

这两个拦截器是默认的拦截器,不用管

里面就3个东西:处理器、拦截器集合、当前执行到第几个拦截器的索引

跨域处理-CorsProcessor

原理:通过CorsProcessor类来处理,其实就是通过response对象来添加响应头。

// 1、判断是否有跨域配置或者是否是预检请求
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
   
    // 2、拿到全局的跨域配置
    CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
    // 3、拿到CrossOrigin级别的
    CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
    // 4、合并
    config = (config != null ? config.combine(handlerConfig) : handlerConfig);
    // 5、处理 CORS 相关的操作
    executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}

1、判断是否有跨域配置或者是否是预检请求

// 是否有跨域配置
protected boolean hasCorsConfigurationSource(Object handler) {
   
    if (handler instanceof HandlerExecutionChain) {
   
        handler = ((HandlerExecutionChain) handler).getHandler();
    }
    return (handler instanceof CorsConfigurationSource || this.corsConfigurationSource != null);
}

// 是否是预检请求
public static boolean isPreFlightRequest(HttpServletRequest request) {
   
    return (HttpMethod.OPTIONS.matches(request.getMethod()) &&
            request.getHeader(HttpHeaders.ORIGIN) != null &&
            request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD) != null);
}

我们的corsConfigurationSource里有值,所以进入跨域的处理

image.png

2、处理 CORS 相关的操作

protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
                                                             HandlerExecutionChain chain, @Nullable CorsConfiguration config) {
   

    // 如果是预检请求就会重写构建一个Handler(HttpRequestHandler)和过滤器链返回
    if (CorsUtils.isPreFlightRequest(request)) {
   
        HandlerInterceptor[] interceptors = chain.getInterceptors();
        return new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
    }
    else {
   
        // 否则就是添加一个CorsInterceptor来做跨域,并且优先级是0
        chain.addInterceptor(0, new CorsInterceptor(config));
        return chain;
    }
}
  • 对于预检请求(Pre-flight requests),会将所选的处理器(handler)替换为一个简单的 HttpRequestHandler, 并由 CorsProcessor 返回空 response。
  • 对于实际的请求(actual requests),会给处理器插入一个 CorsInterceptor(HandlerInterceptor),该HandlerInterceptor 会进行 CORS 相关的检查并添加 CORS 头部信息,也是由CorsProcessor处理

其实就是通过response来添加响应头。

经过这一波操作,我们终于拿到了对应的 HandlerExecutionChain对象。

获取支持处理当前处理器的处理器适配器-getHandlerAdapter()

涉及到的组件:处理器适配器(HandlerAdapter)

终于到了我们 HandlerAdapter 了

拿到对应的 HandlerExecutionChain 对象后,我们就该调用 getHandlerAdapter() 方法来拿能处理我们 Handler 的 HandlerAdapter:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   
    if (this.handlerAdapters != null) {
   
        for (HandlerAdapter adapter : this.handlerAdapters) {
   
            if (adapter.supports(handler)) {
   
                return adapter;
            }
        }
    }
    throw new ServletException("No adapter for handler [" + handler +
                               "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

分析:还是遍历 HandlerAdapter 集合,来调用每一个的 supports() 方法来判断当前是否支持处理这个 Handler。

image.png

其实就是判断当前的这个Handler是不是某一个类。我们直接来看看

各个类的supports()方法:

//*************AbstractHandlerMethodAdapter 和 RequestMappingHandlerAdapter*********
// 我们默认在controller里声明的接口方法就是用的这个适配器来处理
public final boolean supports(Object handler) {
    return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

protected boolean supportsInternal(HandlerMethod handlerMethod) {
    return true;
}


// *******************HandlerFunctionAdapter************************
public boolean supports(Object handler) {
    return handler instanceof HandlerFunction;
}


// *******************HttpRequestHandlerAdapter*********************
public boolean supports(Object handler) {
    return (handler instanceof HttpRequestHandler);
}


//*******************SimpleControllerHandlerAdapter*****************
public boolean supports(Object handler) {
    return (handler instanceof Controller);
}

我们这里RequestMappingHandlerAdapter就能处理,因为我们之前拿到的就是一个HandlerMethod

还有一种继承 AbstractController 类来实现的,就是通过 SimpleControllerHandlerAdapter 来处理的,因为 AbstractController 是 Controller 的子类

至此我们就拿到了对应的 HandlerAdapter,这一步还是比较简单的。

拦截器的前置处理-applyPreHandle()

涉及到的组件:拦截器(HandlerInterceptor)

拦截器的前置处理:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
   
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
   
        for (int i = 0; i < interceptors.length; i++) {
   
            HandlerInterceptor interceptor = interceptors[i];
            if (!interceptor.preHandle(request, response, this.handler)) {
   
                triggerAfterCompletion(request, response, null);
                return false;
            }
            this.interceptorIndex = i;
        }
    }
    return true;
}

分析:就是拿到我们 HandlerExecutionChain 对象里的 HandlerInterceptor 然后遍历调用其前置处理的方法,每处理成功一个就把当前索引赋值给 interceptorIndex 。以便拦截器的最终处理,因为最终处理是一定会执行的。发生错误也会执行。

执行目标方法-ha.handle()

涉及到的组件:参数解析器(HandlerMethodArgumentResolver)、数据绑定器(WebDataBinder)、类型转换器(Converter)、返回值处理器(HandlerMethodReturnValueHandler)、内容协商管理器(ContentNegotiationManager)、消息转换器(HttpMessageConverter)

这块逻辑较多和复杂,就不在这里写了,后面写一篇单独文章来

拦截器的后置处理-applyPostHandle()

涉及到的组件:拦截器(HandlerInterceptor)

拦截器的后置处理

    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
            throws Exception {
   

        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
   
            for (int i = interceptors.length - 1; i >= 0; i--) {
   
                HandlerInterceptor interceptor = interceptors[i];
                // 后置处理
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }
    }

这一次就是倒序处理了。

处理最终结果-processDispatchResult()

涉及到的组件:视图解析器(ViewResolver)、拦截器(HandlerInterceptor)

异常处理逻辑、还会处理ModelAndView、拦截器的最终处理

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                   @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                   @Nullable Exception exception) throws Exception {
   

    boolean errorView = false;

    // 异常处理逻辑
    if (exception != null) {
   
        if (exception instanceof ModelAndViewDefiningException) {
   
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        }
        else {
   
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }

    // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
   
        // 视图解析器渲染结果
        render(mv, request, response);
        if (errorView) {
   
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    else {
   
        if (logger.isTraceEnabled()) {
   
            logger.trace("No view rendering, null ModelAndView returned.");
        }
    }

    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
   
        // Concurrent handling started during a forward
        return;
    }

    if (mappedHandler != null) {
   
        // 拦截器的最终处理
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

由于我们这个是json格式,所以没有 ModelAndView,所以来到拦截器的最终处理:

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
    throws Exception {
   

    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
   
        for (int i = this.interceptorIndex; i >= 0; i--) {
   
            HandlerInterceptor interceptor = interceptors[i];
            try {
   
                // 执行拦截器的最终处理逻辑
                interceptor.afterCompletion(request, response, this.handler, ex);
            }
            catch (Throwable ex2) {
   
                logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
            }
        }
    }
}

执行所有执行过的拦截器的最终处理逻辑

到这里 doDispatch() 方法就执行完毕了。

后记

  1. 执行目标方法的原理
  2. 流程图待画

有机会再填坑吧。

相关文章
|
7月前
|
前端开发
SpringMVC的执行流程
SpringMVC的执行流程
|
3月前
|
XML Java 应用服务中间件
springMVC01,springMVC的执行流程【第一个springMVC例子(XML配置版本):HelloWorld】
通过一个HelloWorld实例,介绍了SpringMVC的基本概念、执行流程,并详细讲解了如何创建和配置第一个SpringMVC项目(基于XML)。
springMVC01,springMVC的执行流程【第一个springMVC例子(XML配置版本):HelloWorld】
|
7月前
|
XML 存储 JSON
SpringMVC执行流程
SpringMVC执行流程
36 0
|
前端开发 Java Spring
浅谈SpringMVC的概念及执行原理
浅谈SpringMVC的概念及执行原理
47 0
|
7月前
|
前端开发 Java 应用服务中间件
SpringMvc拦截器和手写模拟SpringMvc工作流程源码详解
MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分。 M: Model,模型层,指工程中的JavaBean,作用是处理数据。 JavaBean分为两类: 1.实体类Bean:专门存储业务数据的,如Student User等 2.业务处理Bean:指Service或Dao对象,专门用于处理业务逻辑和数据访问。
|
7月前
|
设计模式 开发框架 前端开发
SpringMVC原理分析 | Hello程序
SpringMVC原理分析 | Hello程序
42 0
|
7月前
|
存储 Java
SpringBoot中过滤器如何设置执行顺序
SpringBoot中过滤器如何设置执行顺序
877 0
|
前端开发 Java
springMVC执行流程详解
springMVC执行流程详解
136 0
springMVC执行流程详解
|
存储 JSON 前端开发
你知道 SpringMVC的 执行流程 吗?
你知道 SpringMVC的 执行流程 吗?
124 0
|
前端开发 Java 调度
springMVC执行流程
springMVC执行流程
121 0