前言
SpringMVC 请求处理相信大家都很熟悉了,本篇主要是基于 SpringMVC 处理请求的流程来阅读并调试源码,以及解决几个仅靠流程图无法解释的问题。

关于 Spring MVC 的流程思维导图分享给大家:

Spring 系列的学习笔记和面试题,包含 spring 面试题、spring cloud 面试题、spring boot 面试题、spring 教程笔记、spring boot 教程笔记、最新阿里巴巴开发手册(63 页 PDF 总结)、2020 年 Java 面试手册。一共整理了 1184 页 PDF 文档。

获取这份 1184 页 PDF 文档的 spring 全家桶资料。

本篇使用的 Spring 版本为 5.2.2.RELEASE

九大组件
SpringMVC 几乎所有的功能都由九大组件来完成,所以明白九大组件的作用,对于学习 SpringMVC 来说非常重要。

/** 文件上传解析器 /
private MultipartResolver multipartResolver;
/
* 区域解析器,用于国际化 /
private LocaleResolver localeResolver;
/
* 主题解析器 /
private ThemeResolver themeResolver;
/
* Handler映射信息 /
private List handlerMappings;
/
* Handler适配器*/
private List handlerAdapters;
/** Handler执行异常解析器 /
private List handlerExceptionResolvers;
/
* 请求到视图的转换器 /
private RequestToViewNameTranslator viewNameTranslator;
/
* SpringMVC允许重定向时携带参数,存在session中,用完就销毁,所以叫FlashMap /
private FlashMapManager flashMapManager;
/
* 视图解析器 */
private List viewResolvers;
HandlerMapping:Handler 映射信息,根据请求携带的 url 信息查找处理器(Handler)。每个请求都需要对应的 Handler 处理。

HandlerAdapter:Handler 适配器,SpringMVC 没有直接调用处理器(Handler),而是通过 HandlerAdapter 来调用,主要是为了统一 Handler 的调用方式

ViewResolver:视图解析器,用来将字符串类型的视图名称解析为 View 类型的视图。ViewResolver 需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图自己完成。

MultipartResolver:文件上传解析器,主要用来处理文件上传请求

HandlerExceptionResolver:Handler 执行异常解析器,用来对异常进行统一处理

RequestToViewNameTranslator:请求到视图的转换器

LocaleResolver:区域解析器,用于支持国际化

FlashMapManager:SpringMVC 允许重定向时携带参数,存在 session 中,用完就销毁,所以叫 FlashMap

ThemeResolver:主题解析器,用于支持不同的主题

九大组件中最重的的前三个,HandlerMapping、HandlerAdapter 和 ViewResolver,因为这是阅读源码时,避不开的三个组件。

我把 Spring MVC 相关的技术文章整理成了 PDF,老规矩,关注微信公众号 Java 后端 回复 666 下载。

调试准备
搭建一个基本的 Spring web 项目即可

Controller 部分

@Controllerpublic class IndexController
{
@RequestMapping(“/index/home”)
public String home(String id, Student student,
@RequestParam(“code”) String code)
{
System.out.println(student.getName());
return “index”;
}
@ResponseBody
@RequestMapping(“/index/list”)
public String list()
{
return “success”;
}
}
Entity 部分

public class Student { private String name; private Integer gender; // getter、setter}
还是那句话,Spring 源码非常庞大,不能只见树木不见森林,需要有针对性的阅读,所以本篇只需要关注主体流程即可。

核心方法
我们都知道,SpringMVC 有一个用来分发请求的前端控制器 DispatcherServlet,其中用来处理请求的方法就是 doService,该方法定义如下

doService

/** * Exposes the DispatcherServlet-specific request attributes and delegates to
{
@link
#doDispatch} * for the actual dispatching. */
@Overrideprotected 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
doDispatch 是 doService 中真正用来处理请求的方法

/** * 实际处理请求的方法 */
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)
// 返回值是一个HandlerExecutionChain,也就是处理器执行链 mappedHandler = getHandler(processedRequest);
if (mappedHandler == null)
{
noHandlerFound(processedRequest, response); return; }
// Determine handler adapter for the current request.
// 根据HandlerExecutionChain携带的Handler找到合适的HandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 处理GET请求的缓存 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;
}
}
// 执行拦截器的preHandle方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; }
// Actually invoke the handler.
// 利用HandlerAdapter来执行Handler里对应的处理方法 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted())
{
return;
}
// 如果没有设置视图,则应用默认的视图名 applyDefaultViewName(processedRequest, mv);
// 执行拦截器的postHandle方法 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);
}
}
}
}
该方法就是 SpringMVC 处理请求的整体流程,其中涉及到几个重要的方法。

getHandler
该方法定义如下

/** * Return the HandlerExecutionChain for this request. * 为这个request返回一个HandlerExecutionChain */
@Nullableprotected 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;}
调试信息如下

根据调试信息可以看出,getHandler 方法主要是从 List handlerMappings 集合中遍历查找一个合适的处理器(Handler),返回的结果是一个 HandlerExecutionChain。然后再根据 HandlerExecutionChain 里携带的 Handler 去获取 HandlerAdapter。

getHandlerAdapter
getHandlerAdapter 方法定义如下

/**

  • Return the HandlerAdapter for this handler object. *
    @param handler the handler object to find an adapter for *
    @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
    */
    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”);
    }
    调试信息如下

同样 getHandlerAdapter 方法主要是从 List handlerAdapters 集合中遍历查找一个合适的处理器适配器(HandlerAdapter),返回的结果是一个 HandlerAdapter。

可以看到此处 HandlerAdapter 真正的实现类是 RequestMappingHandlerAdapter。

processDispatchResult
processDispatchResult 方法主要根据方法执行完成后封装的 ModelAndView,转发到对应页面,定义如下

/**

  • Handle the result of handler selection and handler invocation, which is * either a ModelAndView or an Exception to be resolved to a 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)
    {
    // Exception (if any) is already handled… mappedHandler.triggerAfterCompletion(request, response, null);
    }
    }
    render
    render 方法定义如下

/**

  • Render the given ModelAndView. *

    This is the last stage in handling a request. It may involve resolving the view by name. * @param mv the ModelAndView to render *
    @param request current HTTP servlet request *
    @param response current HTTP servlet response *
    @throws ServletException if view is missing or cannot be resolved *
    @throws Exception if there’s a problem rendering the view */
    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception
    {
    // Determine locale for request and apply it to the response. Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
    response.setLocale(locale);
    View view;
    String viewName = mv.getViewName();
    if (viewName != null)
    {
    // We need to resolve the view name.
    // 根据给定的视图名称,解析获取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
    {
    // No need to lookup: the ModelAndView object contains the actual View object. 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() + “'”);
    }
    }
    // Delegate to the View object for rendering. 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;
    }
    }
    resolveViewName
    resolveViewName 方法定义如下

@Nullableprotected View resolveViewName(String viewName,
@Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception
{
if (this.viewResolvers != null)
{
for (ViewResolver viewResolver : this.viewResolvers)
{
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null)
{
return view;
}
}
}
return null;
}
调试信息如下

根据调试信息可以看到真正解析视图的 ViewResolver 的是 InternalResourceViewResolver 类,也就是我们经常配置的一项类型

至此我们就得到了 SpringMVC 处理请求的完整逻辑

SpringMVC 处理请求的整个流程已经梳理清楚了。

但是,有两个重要的问题没有解决,那就是:参数绑定和返回值处理。

因为在编写 Controller 里面的方法的时候,各种类型的参数都有,SpringMVC 是怎么处理不同类型的参数的呢? > SpringMVC 处理请求完成后,一定会返回 ModelAndView 吗,如果加了 @ResponseBody 注解呢?

参数绑定
在整个流程中,还有一个最重要的方法,那就是真正执行 handler 的方法,参数的绑定和返回值的处理都在这个方法里,也就是

// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
handle
handle 方法的作用是根据请求参数,执行真正的处理方法,并且返回合适的 ModelAndView 对象,也有可能返回 null。该方法定义如下 在 AbstractHandlerMethodAdapter 类中

/** * This implementation expects the handler to be an
{
@link HandlerMethod}. */
@Override
@Nullablepublic final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
return handleInternal(request, response, (HandlerMethod) handler);
}
可以看到这个方法实现只有一行代码

handleInternal
继续深入 handleInternal 方法

@Overrideprotected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception
{
ModelAndView mav;
// 校验指定的请求以获取受支持的方法类型(GET、POST等)和所需的session 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);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL))
{
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes())
{
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else { prepareResponse(response);
}
}
return mav;
}
invokeHandlerMethod

继续深入 invokeHandlerMethod 方法

/**

  • Invoke the
    {
    @link RequestMapping} handler method preparing a
    {
    @link ModelAndView} * if view resolution is required. * 执行
    @RequestMapping标注的handler方法,如果需要解析视图就准备一个ModelAndView */
    @Nullableprotected 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接口封装执行方法的信息,提供对方法参数,方法返回值,方法注释等的便捷访问。 ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    if (this.argumentResolvers != null)
    {
    invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    }
    if (this.returnValueHandlers != null)
    {
    invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    } invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    // ModelAndViewContainer可以看做ModelAndView的上下文容器,关联着Model和View的信息 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(as…
    【这里想说,因为自己也走了很多弯路过来的,所以才下定决心整理,收集过程虽不易,但想到能帮助到一部分自学java 的人,心里也是甜的!有需要的伙伴请点㊦方】↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

Spring MVC 请求处理过程。你这样回答保证通过面试!相关推荐

  1. java request 处理过程_小猿圈Java开发之从代码看spring mvc请求处理过程

    原标题:小猿圈Java开发之从代码看spring mvc请求处理过程 Java作为编程界的常青树,有自己生存的独到之处,小猿圈java讲师今天就分享一个关于从代码看spring mvc请求处理过程,通 ...

  2. spring MVC请求处理类注解属性详解

    spring MVC请求处理类注解属性详解

  3. java处理请求的流程_Java Spring mvc请求处理流程详解

    Spring mvc请求处理流程详解 前言 spring mvc框架相信很多人都很熟悉了,关于这方面的资料也是一搜一大把.但是感觉讲的都不是很细致,让很多初学者都云里雾里的.本人也是这样,之前研究过, ...

  4. 初探Spring MVC 请求处理流程

    Spring MVC 相对于前面的章节算是比较简单的,我们首先引用<Spring in Action>上的一张图来了解Spring MVC 的核心组件和大致处理流程: 从上图中看到 ①.D ...

  5. Spring MVC请求处理流程分析

    2019独角兽企业重金招聘Python工程师标准>>> 一.简介 Spring MVC框架在工作中经常用到,配置简单,使用起来也很方便,很多书籍和博客都有介绍其处理流程,但是,对于其 ...

  6. Spring MVC请求处理流程

    从web.xml中 servlet的配置开始, 根据servlet拦截的url-parttern,来进行请求转发 Spring MVC工作流程图 图一 图二  Spring工作流程描述 1. 用户向服 ...

  7. Spring MVC 执行过程原理(请求映射原理、参数处理原理、返回值处理器)

    Spring MVC 执行过程分析 文章目录 Spring MVC 执行过程分析 请求映射原理 适配器Adapter 执行目标方法 参数处理器解析器HandlerMethodArgumentResol ...

  8. Spring MVC 教程详解 个人总结 复习必备 面试宝典 狂神笔记

    文章目录 一.MVC 模式 1.什么是 MVC 2.Servlet MVC 小结 二.Spring MVC 1.Spring MVC 概念 为什么学习 Spring MVC 中央控制器 Dispatc ...

  9. Spring MVC(一)

    Spring MVC 一 Spring MVC应用 附 mvc web项目搭建 第⼀部分 Spring MVC 应⽤ 第 1 节 Spring MVC 简介 1.1 MVC 体系结构 1.2 Spri ...

最新文章

  1. ActiveReports 报表应用教程 (14)---数据可视化
  2. Leangoo阶段式硬件产品开发流程
  3. 产品操作-查询全部产品
  4. ui设计 网络错误_UI设计人员常犯的10个错误
  5. 2016微软开发者峰会在京举办 纳德拉要来做演讲
  6. leetcode53. 最大子数组和(动态规划)
  7. Spring MVC + Hibernate + Maven:CRUD操作示例
  8. 4个空格和一个tab有什么区别_为什么有时候会放屁连连?这4个原因,一个都别放过...
  9. 好文!2020届最新互联网校招薪资大全!
  10. 上传文件时服务器返回错误信息,上传文件时的错误提示
  11. 修改配置文件,编译freeswitch支持H264
  12. FbinstTools制作多系统启动U盘(Windows+Linux)
  13. java 毕向东 内部类_毕向东Java视频学习笔记【Day10 多态+内部类】
  14. 【python基础】第02回 计算机基础2
  15. PyTorch创建自己的图像分类数据集
  16. 能上QQ,不能打开网页
  17. Epic下载速度慢 Epic下载不稳定怎么办【解决办法】
  18. HDU 6069 Counting Divisors
  19. linux上的两种可执行程序
  20. 【转载精品】Web Service到底是什么?

热门文章

  1. 使用Bash编写Linux Shell脚本-4.脚本初探
  2. 数学家西蒙斯:华尔街最赚钱的基金经理
  3. android项目中文字乱码的问题
  4. Unicode 14 标准发布
  5. 那些证书相关的玩意儿(SSL,X.509,PEM,DER,CRT,CER,KEY,CSR,P12等)【CSR文件 和 PEM 文件什么区别】
  6. 搭建阿里云服务器,实现服务端与客户端socket数据通信(详细版)
  7. Nginx输出header到access日志文件
  8. 在虚拟机中使用Ubuntu和windows系统
  9. 自动售货机html代码,自动售货机系统源代码.doc
  10. 日本Java18_死磕18个Java8日期处理,工作必用!