码农翻身

Spring MVC 篇之请求处理流程

- by MRyan, 2023-01-26



本系列是针对 Spring-Framwork 5.1x 的源码分析

文章已收录至精进Spring MVC系列 系列其它文章 https://www.wormholestack.com/tag/Spring-MVC/

源码阅读环境:https://gitee.com/M-Analysis/source_springframwork5.1x 已填充关键注释

建议阅读本篇前先阅读上篇文章《Spring MVC 篇之容器启动》


1. Spring MVC 结构体系

Spring MVC 框架是一个基于请求驱动的 Web 框架,并且也使用了前端控制器模式来进行设计,再根据请求映射 规则分发给相应的页面控制器(动作/处理器)进行处理。

Spring MVC 为三层架构:表现层、业务层、持久层,MVC 是模型(model),视图(view),控制器(controller) 的缩写,

分层是为了解耦,解耦是为了每⼀层只编写⾃⼰的东⻄,不编写任何其他的代码,职责分明便于维护和分工。

MVC 中每个部分各司其职:

  • Model(模型)包含业务模型和数据模型,数据模型⽤于封装数据,业务模型⽤于处理业务。
  • View(视图)通常指的就是我们的 jsp 或者 html。作⽤⼀般就是展示数据的。通常视图是依据模型数据创建的。
  • Controller(控制器)是应⽤程序中处理⽤户交互的部分。作⽤⼀般就是处理程序逻辑的。

image-20230126131321030

2. 九大组件初始化

上篇文章我们了解了 Spring MVC 父子容器的创建及初始化。其中也在子 Servlet 容器初始化的源码分析中提到了一嘴九大组件的初始化,不知道你还有没有印象,上篇文章说到在子 Servlet 容器启动后会发布一个完成事件,会触发广播分发事件,其中一个事件就是 SourceFilteringListener ,最终会调用DispatcherServlet 的 onRefresh 方法来注册九大组件。

// DispatcherServlet.java

  @Nullable
  private MultipartResolver multipartResolver;

  @Nullable
  private LocaleResolver localeResolver;

  @Nullable
  private ThemeResolver themeResolver;
 
  @Nullable
  private List<HandlerMapping> handlerMappings;

  @Nullable
  private List<HandlerAdapter> handlerAdapters;

  @Nullable
  private List<HandlerExceptionResolver> handlerExceptionResolvers;

  @Nullable
  private RequestToViewNameTranslator viewNameTranslator;

  @Nullable
  private FlashMapManager flashMapManager;

  @Nullable
  private List<ViewResolver> viewResolvers;
    
   @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

  // 初始化 Spring MVC 9大组件
  protected void initStrategies(ApplicationContext context) {
        // 初始化 MultipartResolver
        initMultipartResolver(context);
        // 初始化 LocaleResolver
        initLocaleResolver(context);
        // 初始化 ThemeResolver
        initThemeResolver(context);
        // 初始化 HandlerMappings
        initHandlerMappings(context);
        // 初始化 HandlerAdapters
        initHandlerAdapters(context);
        // 初始化 HandlerExceptionResolvers
        initHandlerExceptionResolvers(context);
        // 初始化 RequestToViewNameTranslator
        initRequestToViewNameTranslator(context);
        // 初始化 ViewResolvers
        initViewResolvers(context);
        // 初始化 FlashMapManager
        initFlashMapManager(context);
    }

值得一提的是这九大组件都是面向接口定义,提供了强大的自定义能力。

初始化的过程首先从容器中获取,如果容器中没有配置组件,则使用默认值,请参考 DispatcherServlet.properties 文件配置,感兴趣的同学可以自行研究

,


下面依次来介绍下这九大组件

2.1 MultipartResolver

MultipartResolver ⽤于上传请求,通过将普通的请求包装成 MultipartHttpServletRequest 来实现

public interface MultipartResolver {

/**
     * 是否为 multipart 请求
     */
    boolean isMultipart(HttpServletRequest request);

    /**
     * 将 HttpServletRequest 请求封装成 MultipartHttpServletRequest 对象
     */
    MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;

    /**
     * 清理处理 multipart 产生的资源,例如临时文件
     *
     */
    void cleanupMultipart(MultipartHttpServletRequest request);

}

当内容类型 Content-Type 为 multipart 的请求的解析器接口,当接收到文件上传的请求时,MultipartResolver 会将 HttpServletRequest 封装成 MultipartHttpServletRequest ,这样从 MultipartHttpServletRequest 中获得上传的文件,

Spring MVC 提供标准的 MultipartResolver 代码如下

public class StandardServletMultipartResolver implements MultipartResolver {

    private boolean resolveLazily = false;

    public void setResolveLazily(boolean resolveLazily) {
        this.resolveLazily = resolveLazily;
    }


    @Override
    public boolean isMultipart(HttpServletRequest request) {
        return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
    }

    @Override
    public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
        return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
    }

    @Override
    public void cleanupMultipart(MultipartHttpServletRequest request) {
        if (!(request instanceof AbstractMultipartHttpServletRequest) ||
                ((AbstractMultipartHttpServletRequest) request).isResolved()) {
        try {
                for (Part part : request.getParts()) {
                    if (request.getFile(part.getName()) != null) {
                        part.delete();
                    }
                }
            }
            catch (Throwable ex) {
                LogFactory.getLog(getClass()).warn("Failed to perform cleanup of multipart items", ex);
            }
        }
    }

}

2.2 LocaleResolver

本地化(国际化)解析器接口,ViewResolver 组件的 resolveViewName ⽅法需要两个参数,⼀个是视图名,⼀个是 Locale。LocaleResolver ⽤于从请求中解析出 Locale,⽐如中国 Locale 是 zh-CN,⽤来表示⼀个区域。这个组件也是 i18n 的基础

public interface LocaleResolver {

    /**
     * 从请求中,解析出要使用的语言。例如,请求头的 "Accept-Language"
     */
    Locale resolveLocale(HttpServletRequest request);

    /**
     * 设置请求所使用的语言
     */
    void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);

}

2.3 ThemeResolver

主题解析器接口

public interface ThemeResolver {

    /**
     * 从请求中,解析出使用的主题。例如,从请求头 User-Agent ,判断使用 PC 端,还是移动端的主题
     */
    String resolveThemeName(HttpServletRequest request);

    /**
     * 设置请求,所使用的主题。
     */
    void setThemeName(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable String themeName);

}

2.4 HandlerMapping

处理器匹配接口,标注了 @RequestMapping 的每个⽅法都可以看成是⼀个 Handler。Handler负责具体实际的请求处理,在请求到达后,HandlerMapping 的作⽤便是找到请求相应的处理器Handler 和 Interceptor

简单的来说其实就是内部维护了一些 <访问路径, 处理器> 映射,负责为请求找到合适的处理器。

public interface HandlerMapping {

    String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
  
    String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
  
    String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
  
    String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
  
    String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
  
    String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

    /**
     * 获得请求对应的处理器和拦截器们
     */
    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

}

返回的对象类型是 HandlerExecutionChain ,它包含处理器和拦截器 HandlerInterceptor 集合

// HandlerExecutionChain.java

/**
 * 处理器
 */
private final Object handler;

    @Nullable
    private HandlerInterceptor[] interceptors;

    @Nullable
    private List<HandlerInterceptor> interceptorList;

2.5 HandlerAdapter

处理器适配器接口,Spring MVC 中 Handler 可以是任意形式的,只要能处理请求即可。但是把请求交给 Servlet 的时候,由于 Servlet 的⽅法结构都是 doService(HttpServletRequest req,HttpServletResponse resp) 形式的,要让固定的 Servlet 处理⽅法调⽤ Handler 来进⾏处理,便是 HandlerAdapter 的职责

public interface HandlerAdapter {

    /**
     * 是否支持该处理器
     */
    boolean supports(Object handler);

    /**
     * 执行处理器,返回 ModelAndView 结果
     */
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    /**
     * 返回请求的最新更新时间。
     *
     * 如果不支持该操作,则返回 -1 即可
     */
    long getLastModified(HttpServletRequest request, Object handler);

}

刚刚分析的处理器 handler 的类型是 Object 类型,HandlerAdapter 的用途就是适配处理器

2.6 HandlerExceptionResolver

处理器异常解析器接口,将处理器 Handler 执行时发生的异常,解析转换成对应的 ModelAndView,之后交给渲染⽅法进⾏渲染,渲染⽅法会将 ModelAndView 渲染成⻚⾯

public interface HandlerExceptionResolver {

    /**
     * 解析异常,转换成对应的 ModelAndView 结果
     */
    @Nullable
    ModelAndView resolveException(
            HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);

}

2.7 RequestToViewNameTranslator

请求到视图名的转换器接口,作用是从请求中获取 ViewName.因为 ViewResolver 根据 ViewName 查找 View,但有的 Handler 处理完成之后,没有设置 View,也没有设置 ViewName,便要通过这个组件从请求中查找 ViewName。

public interface RequestToViewNameTranslator {

    /**
     * 根据请求,获得其视图名
     */
    @Nullable
    String getViewName(HttpServletRequest request) throws Exception;

}

2.8 ViewResolver

实体解析器接口,⽤于将 String 类型的视图名和 Locale 国际化解析为 View 类型的视图,只有⼀个resolveViewName()⽅法。从⽅法的定义可以看出,Controller 层返回的 String 类型视图名viewName 最终会在这⾥被解析成为 View。View 是⽤来渲染⻚⾯的,也就是说,它会将程序返回的参数和数据填⼊模板中,⽣成 html ⽂件

实现类有 InternalResourceViewResolver 负责解析 JSP 视图,FreeMarkerViewResolver 负责解析 Freemarker 视图

public interface ViewResolver {

    /**
     * 根据视图名和国际化,获得最终的 View 对象
     */
    @Nullable
    View resolveViewName(String viewName, Locale locale) throws Exception;

}

2.9 FlashMapManager

FlashMap 管理器接口,负责重定向时,保存参数到临时存储中,

FlashMap ⽤于重定向时的参数传递,⽐如在处理⽤户订单时候,为了避免重复提交,可以处理完 post 请求之后重定向到⼀个 get 请求,这个 get 请求可以⽤来显示订单详情之类的信息。

这样做虽然可以规避⽤户重新提交订单的问题,但是在这个⻚⾯上要显示订单的信息,这些数据从哪⾥来获得呢?因为重定向时没有传递参数这⼀功能的,如果不想把参数写进URL(不推荐),那么就可以通过 FlashMap 来传递。

只需要在重定向之前将要传递的数据写⼊请求(可以通过 ServletRequestAttributes.getRequest()⽅法获得)的属性 OUTPUT_FLASH_MAP_ATTRIBUTE 中,这样在重定向之后的 Handler 中 Spring 就会⾃动将其设置到 Model 中,在显示订单信息的⻚⾯上就可以直接从 Model 中获取数据。FlashMapManager 就是⽤来管理 FalshMap 的。

public interface FlashMapManager {

    /**
     * 恢复参数,并将恢复过的和超时的参数从保存介质中删除
     */
    @Nullable
    FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);

    /**
     * 将参数保存起来
     */
    void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);

}

默认情况下,这个临时存储会是 Session 。也就是说:

  • 重定向前,保存参数到 Seesion 中
  • 重定向后,从 Session 中获得参数,并移除


简单解析了九大组件的基本功能,下面我们抽象思维,从一个请求链路为入口,串联起所有组件的功能,来了解 Spring MVC 是如何处理请求的。

3. 请求链路

首先,当用户的浏览器发出了一个请求,请求经过互联网到达了我们的服务器。Servlet 容器首先接待了这个请求,也就是 DispatcherServlet。

下图为 DispatcherServlet 继承关系图

image-20230126150009069

实际上,FrameworkServlet 才是真正的入口点。来看下 FrameworkServlet 的实现。

// 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 {
            // 父类 service 方法 处理其它请求
            super.service(request, response);
        }
    }

代码【1】处调用了父类 HttpServlet 的 service 方法

// HttpServlet.java
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) {
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    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 {
           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);
        }
    }

service 方法中会根据 method 的类型调用父类 FrameworkServlet 实现

  • doGet(HttpServletRequest request, HttpServletResponse response)
  • doPost(HttpServletRequest request, HttpServletResponse response)
  • doPut(HttpServletRequest request, HttpServletResponse response)
  • doDelete(HttpServletRequest request, HttpServletResponse response)
  • doOptions(HttpServletRequest request, HttpServletResponse response)
  • doTrace(HttpServletRequest request, HttpServletResponse response)
  • service(HttpServletRequest request, HttpServletResponse response)

这些实现,最终会调用 processRequest(HttpServletRequest request, HttpServletResponse response) 方法,处理请求。

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

        processRequest(request, response);
    }
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 记录当前时间,用于计算 web 请求的处理时间
        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 {
            // 【1】执行真正的逻辑
            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();
            }
            // 打印请求日志,并且日志级别为 DEBUG
            logResult(request, response, failureCause, asyncManager);
            // 布 ServletRequestHandledEvent 事件
            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

代码【1】处调用了doService(request, response); 这是真正执行逻辑,是个抽象方法

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

该抽象方法由 DispatcherServlet 实现,所以这就是 DispatcherServlet 处理请求的真正入口

// DispatcherServlet.java
@Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 打印请求日志,并且日志级别为 DEBUG
        logRequest(request);

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

        // 【1】设置 Spring 框架中的常用对象到 request 属性中
        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 {
            // 【2】执行请求的分发
            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);
                }
            }
        }
    }

先来看代码【1】处,将 Spring 框架中的常用对象到 request 的属性中。


代码【2】处,真正的执行请求分发,来看下 doDispatch 方法代码

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
    // Handler 目标方法执行链
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

      // 对异步请求的支持,Servlet 3.0 以后才有的 Webflux
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

            try {
        // 是否文件上传
                processedRequest = checkMultipart(request);
        // 针对文件上传的特殊处理
                multipartRequestParsed = (processedRequest != request);

                // 【A】获得请求对应的 HandlerExecutionChain 对象
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    // 如果获取不到,则根据配置抛出异常或返回 404 错误
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 【B】获得当前 handler 对应的 HandlerAdapter 对象
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

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

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

                // 【D】真正的调用 handler 方法,并返回视图
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                
        // 【E】
                applyDefaultViewName(processedRequest, mv);
                // 【F】后置处理 拦截器
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                // 记录异常,只记录异常,不做异常抛出统一交由Exception
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            // 【G】处理正常和异常的请求调用结果。
            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()) {
            if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }


代码【A】处调用 getHandler(HttpServletRequest request) 方法,返回请求对应的是 HandlerExecutionChain 对象,它包含处理器和拦截器集合

@Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            // 遍历 HandlerMapping 数组
            for (HandlerMapping mapping : this.handlerMappings) {
                // 获得请求对应的 HandlerExecutionChain 对象
                HandlerExecutionChain handler = mapping.getHandler(request);
                // 获得后直接返回
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

mapping.getHandler(request); 处调用了基类 AbstractHandlerMapping 的 getHandler 方法

// AbstractHandlerMapping.java
@Override
    @Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // 获得处理器 ,HandlerMapping 的 registry 中寻找映射,返回真正执行当前请求方法的 HandlerMethod
        Object handler = getHandlerInternal(request);
        // 获得不到,则使用默认处理器
        if (handler == null) {
            handler = getDefaultHandler();
        }
        // 还是获得不到,则返回 null
        if (handler == null) {
            return null;
        }
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
        }

        // 构造处理器链
        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 (CorsUtils.isCorsRequest(request)) {
            CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }

        return executionChain;
    }

由子类 AbstractHandlerMethodMapping 实现抽象方法 getHandlerInternal

    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        this.mappingRegistry.acquireReadLock();
        try {
            // 找到 HandlerMethod
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        } finally {
            this.mappingRegistry.releaseReadLock();
        }
    }


    @Nullable
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        // Match 数组,存储匹配上当前请求的结果
        List<Match> matches = new ArrayList<>();
        // 优先,基于直接 URL 的 Mapping 们,进行匹配
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            addMatchingMappings(directPathMatches, matches, request);
        }
        // 其次,扫描注册表的 Mapping 们,进行匹配
        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }

        // 如果匹配到,则获取最佳匹配的 Match 对象的 handlerMethod 属性
        if (!matches.isEmpty()) {
            // 创建 MatchComparator 对象,排序 matches 结果
            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
            matches.sort(comparator);
            // 获得首个 Match 对象
            Match bestMatch = matches.get(0);
            // 处理存在多个 Match 对象的情况
            if (matches.size() > 1) {
                if (logger.isTraceEnabled()) {
                    logger.trace(matches.size() + " matching mappings: " + matches);
                }
                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }
                Match secondBestMatch = matches.get(1);
                // 比较 bestMatch 和 secondBestMatch
                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);
            // 处理首个 Match 对象
            handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        } else {
            // 如果匹配不到,则处理不匹配的情况
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }

来看 lookupHandlerMethod 的具体实现,从 MappingRegistry 中获取 mappingmtcher,最终经过 handleMatch 匹配到对应的 HandlerMethod。

你也许会好奇 MappingRegistry 是什么,可以看出它相当于 Mapping 映射的注册表

class MappingRegistry {
  /**
         * 注册表 
         * key: Mapping
         */
        private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

        /**
         * 注册表2
         * key:Mapping
         */
        private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

        /**
         * 直接 URL 的映射
         * <p>
         * key:直接 URL
         * value:Mapping 数组
         */
        private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();

        /**
         * Mapping 的名字与 HandlerMethod 的映射
         * <p>
         * key:Mapping 的名字
         * value:HandlerMethod 数组
         */
        private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

        private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

        /**
         * 读写锁
         */
        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

}

不知道你有没有想过刚刚我们分析的是直接从 MappingRegistry 中获取,那 MappingRegistry 中的数据是何时存在的呢?

简单分析下:

因为 AbstractHandlerMethodMapping 类实现了 InitializingBean,由 Spring 生命周期推动,所以接受到了容器的回调,完成了处理器方法集合的初始化。

// AbstractHandlerMethodMapping.java    
  @Override
    public void afterPropertiesSet() {
        // 初始化处理器方法集合
        initHandlerMethods();
    }

   protected void initHandlerMethods() {
        // 获取子 Web 容器所有的组件
        for (String beanName : getCandidateBeanNames()) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                processCandidateBean(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

    protected void processCandidateBean(String beanName) {
        Class<?> beanType = null;
        try {
            beanType = obtainApplicationContext().getType(beanName);
        } catch (Throwable ex) {
            if (logger.isTraceEnabled()) {
                logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
            }
        }
        // isHandler 是否注解标注为 RequestMapping 或者 Controller
        if (beanType != null && isHandler(beanType)) {
            // 监测 HandlerMethods
            detectHandlerMethods(beanName);
        }
    }

protected void detectHandlerMethods(Object handler) {
        // 获得处理器类型
        Class<?> handlerType = (handler instanceof String ?
                obtainApplicationContext().getType((String) handler) : handler.getClass());

        if (handlerType != null) {
            // 获得真实的类。因为,handlerType 可能是代理类
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            // 探索当前类中满足条件的所有方法 RequestMapping
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                    (MethodIntrospector.MetadataLookup<T>) method -> {
                        try {
                            return getMappingForMethod(method, userType);
                        } catch (Throwable ex) {
                            throw new IllegalStateException("Invalid mapping on handler class [" +
                                    userType.getName() + "]: " + method, ex);
                        }
                    });
            if (logger.isTraceEnabled()) {
                logger.trace(formatMappings(userType, methods));
            }
            // 遍历方法,逐个注册 HandlerMethod
            methods.forEach((method, mapping) -> {
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                // 注册进处理器注册中心中
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }

    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
        // 注册
        this.mappingRegistry.register(mapping, handler, method);
    }

可以看到首先获取到了子容器中的所有组件,并探测了所有满足被 RequestMapping 或者 Controller 注解注释的方法,将其注册至 MappingRegistry

public void register(T mapping, Object handler, Method method) {
            // 获得写锁
            this.readWriteLock.writeLock().lock();
            try {
                // 创建 HandlerMethod 对象
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                // 校验当前 mapping 不存在,否则抛出 IllegalStateException 异常
                assertUniqueMethodMapping(handlerMethod, mapping);
                // 添加 mapping + HandlerMethod 到 mappingLookup 中
                this.mappingLookup.put(mapping, handlerMethod);

                // 获得 mapping 对应的普通 URL 数组
                List<String> directUrls = getDirectUrls(mapping);
                // 添加到 url + mapping 到 urlLookup 集合中
                for (String url : directUrls) {
                    this.urlLookup.add(url, mapping);
                }

                // 初始化 nameLookup
                String name = null;
                if (getNamingStrategy() != null) {
                    // 获得 Mapping 的名字
                    name = getNamingStrategy().getName(handlerMethod, mapping);
                    // 添加到 mapping 的名字 + HandlerMethod 到 nameLookup 中
                    addMappingName(name, handlerMethod);
                }

                CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
                if (corsConfig != null) {
                    this.corsLookup.put(handlerMethod, corsConfig);
                }

                // 创建 MappingRegistration 对象,并 mapping + MappingRegistration 添加到 registry 中
                this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
            } finally {
                // 释放写锁
                this.readWriteLock.writeLock().unlock();
            }
        }


代码【B】处getHandlerAdapter(mappedHandler.getHandler());,调用 getHandlerAdapter(Object handler) 方法,获得当前 handler 对应的 HandlerAdapter 对象,以下是 getHandlerAdapter 的实现

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            // 遍历 HandlerAdapter 数组
            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");
    }


代码【C】代码【F】处,做了拦截器的前置处理和后置处理,分别调用了interceptor.preHandle(request, response, this.handler)interceptor.postHandle(request, response, this.handler, mv)


代码【D】处调用 HandlerAdapter#handle(HttpServletRequest request, HttpServletResponse response, Object handler) 方法,真正的调用 handler 方法,并返回视图。这里,一般就会调用我们定义的 Controller 的方法


代码【E】处调用 applyDefaultViewName(HttpServletRequest request, ModelAndView mv) 方法,当无视图的情况下,设置默认视图

// DispatcherServlet.java
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
    // 无视图
        if (mv != null && !mv.hasView()) {
      // 获得默认视图
            String defaultViewName = getDefaultViewName(request);
      // 设置默认视图
            if (defaultViewName != null) {
                mv.setViewName(defaultViewName);
            }
        }
    }

    @Nullable
    protected String getDefaultViewName(HttpServletRequest request) throws Exception {
        return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
    }


代码【G】处 处理正常和异常的请求调用结果,调用 processDispatchResult 方法

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                       @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                       @Nullable Exception exception) throws Exception {
        // 标记,是否是生成的 ModelAndView 对象
        boolean errorView = false;

        // 如果是否异常的结果
        if (exception != null) {
            // 从 ModelAndViewDefiningException 中获得 ModelAndView 对象
            if (exception instanceof ModelAndViewDefiningException) {
                logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException) exception).getModelAndView();
            } else {
                // 处理异常,生成 ModelAndView 对象
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                mv = processHandlerException(request, response, handler, exception);
                // 标记 errorView
                errorView = (mv != null);
            }
        }

        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()) {
            return;
        }

        // 已完成处理 拦截器
        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }


protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // Determine locale for request and apply it to the response.
        // 从 request 中获得 Locale 对象,并设置到 response 中
        Locale locale =
                (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
        response.setLocale(locale);

        // 获得 View 对象
        View view;
        String viewName = mv.getViewName();
        // 使用 viewName 获得 View 对象
        if (viewName != null) {
            // 使用 viewName 获得 View 对象
            view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
            if (view == null) {
                throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                        "' in servlet with name '" + getServletName() + "'");
            }
        } else {
            // 直接使用 ModelAndView 对象的 View 对象
            view = mv.getView();
            if (view == null) {
                throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                        "View object in servlet with name '" + getServletName() + "'");
            }
        }

        if (logger.isTraceEnabled()) {
            logger.trace("Rendering view [" + view + "] ");
        }
        try {
            // 设置响应的状态码
            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
            // 渲染页面
            view.render(mv.getModelInternal(), request, response);
        } catch (Exception ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Error rendering view [" + view + "]", ex);
            }
            throw ex;
        }
    }

由处理器生成 ModelAndView 对象后渲染 View 视图。

最后将相应交由 DispatcherServlet 返回给用户。

至此一个请求流程结束。

4.小结

我画了一张图,简单总结下帮助理解

image-20230126163140354

  1. 首先用户发送请求由 DispatcherServlet 接受处理,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制.
  2. HandlerMapping 将会把请求映射为 HandlerExecutionChain 执行链对象(包含一个Handler 处理器(页面控制器)对象、多个HandlerInterceptor 拦截器对象),通过这种策略模式,很容易添加新的映射策略.
  3. HandlerAdapter 将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器.
  4. 请求交由 HandlerAdapter 将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个 ModelAndView 对象(包含模型数据、逻辑视图名).
  5. 请求交由 ViewResolver,ViewResolver 将把 ModelAndView 的逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术.
  6. 渲染View视图,View 会根据传进来的 Model 模型数据进行渲染,此处的 Model 实际是一个 Map 数据结构,因此 很容易支持其他视图技术.
  7. 返回控制权给 DispatcherServlet,由DispatcherServlet 返回响应给用户,到此一个流程结束。

参考文章

https://cloud.tencent.com/developer/article/1649425

作者:MRyan


本文采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
转载时请注明本文出处及文章链接。本文链接:https://wormholestack.com/archives/667/
2025 © MRyan 116 ms