Spring MVC 处理一个请求的流程分析
Spring MVC是Spring系列框架中使用频率最高的部分。不管是Spring Boot还是传统的Spring项目,只要是Web项目都会使用到Spring MVC部分。因此程序员一定要熟练掌握MVC部分。本篇博客简要分析Spring MVC处理一个请求的流程。
一个请求从客户端发出到达服务器,然后被处理的整个过程其实是非常复杂的。本博客主要介绍请求到达服务器被核心组件DispatcherServlet处理的整理流程(不包括Filter的处理流程)。
1. 处理流程分析
Servlet处理一个请求时会调用service()方法,所以DispatcherServlet处理请求的方式也是从service()方法开始(debug的话建议从DispatcherServlet的service方法开始debug)。FrameworkServlet重写了HttpServlet的service方法,这个service方法后面又调用了FrameworkServlet的processRequest()方法,processRequest()调用了DispatcherServlet的doService()方法,最后调用到DispatcherServlet的doDispatcher()方法。整合处理请求的方法调用流程如上,下面看下代码:
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
processRequest(request, response);
}
else {
//这边调用了HttpServlet的service()方法,但由于FrameWorkServle重写了doGet、doPost等方法,所以最终还是会调用到processRequest方法
super.service(request, response);
}
}
再看看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 {
//这边调用DispatcherServlet的doService()方法
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (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();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
doService()方法的具体内容会在后面讲到,这边描述下doDispatcher()的内容,参考了博客:
首先根据请求的路径找到HandlerMethod(带有Method反射属性,也就是对应Controller中的方法),然后匹配路径对应的拦截器,有了HandlerMethod和拦截器构造个HandlerExecutionChain对象。HandlerExecutionChain对象的获取是通过HandlerMapping接口提供的方法中得到。有了HandlerExecutionChain之后,通过HandlerAdapter对象进行处理得到ModelAndView对象,HandlerMethod内部handle的时候,使用各种HandlerMethodArgumentResolver实现类处理HandlerMethod的参数,使用各种HandlerMethodReturnValueHandler实现类处理返回值。 最终返回值被处理成ModelAndView对象,这期间发生的异常会被HandlerExceptionResolver接口实现类进行处理。
总结下Spring MVC处理一个请求的过程:
- 首先,搜索应用的上下文对象 WebApplicationContext 并把它作为一个属性(attribute)绑定到该请求上,以便控制器和其他组件能够使用它。
- 将地区(locale)解析器绑定到请求上,以便其他组件在处理请求(渲染视图、准备数据等)时可以获取区域相关的信息。如果你的应用不需要解析区域相关的信息;
- 将主题(theme)解析器绑定到请求上,以便其他组件(比如视图等)能够了解要渲染哪个主题文件。同样,如果你不需要使用主题相关的特性,忽略它即可如果你配置了multipart文件处理器,那么框架将查找该文件是不是multipart(分为多个部分连续上传)的。若是,则将该请求包装成一个 MultipartHttpServletRequest 对象,以便处理链中的其他组件对它做进一步的处理。关于Spring对multipart文件传输处理的支持;
- 为该请求查找一个合适的处理器。如果可以找到对应的处理器,则与该处理器关联的整条执行链(前处理器、后处理器、控制器等)都会被执行,以完成相应模型的准备或视图的渲染如果处理器返回的是一个模型(model),那么框架将渲染相应的视图。若没有返回任何模型(可能是因为前后的处理器出于某些原因拦截了请求等,比如,安全问题),则框架不会渲染任何视图,此时认为对请求的处理可能已经由处理链完成了(这个过程就是doService()和doDispatcher()做的事情)
1、 首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;
2、 DispatcherServlet——>HandlerMapping,HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象,通过这种策略模式,很容易添加新的映射策略;
3、 DispatcherServlet——>HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;
4、 HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);
5、 ModelAndView的逻辑视图名——> ViewResolver,ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
6、 View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;
7、返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。
2. 请求流程图

还是这个图比较清楚。发现根据代码不太能把这个流程说清楚。而且整个流程很长,代码很多,我就不贴代码了。这里根据这个图再把整个流程中组件的功能总结下:
DispatcherServlet:核心控制器,所有请求都会先进入DispatcherServlet进行统一分发,是不是感觉有点像外观模式的感觉;HandlerMapping:这个组件的作用就是将用户请求的URL映射成一个HandlerExecutionChain。这个HandlerExecutionChain是HandlerMethod和HandlerInterceptor的组合。Spring在启动的时候会默认注入很多HandlerMapping组件,其中最常用的组件就是RequestMappingHandlerMapping。上面的
HandlerMethod和HandlerInterceptor组件分别对应我们Controller中的方法和拦截器。拦截器会在HandlerMethod方法执行之前执行HandlerAdapter组件,这个组件的主要作用是用来对HandlerMethod中参数的转换,对方法的执行,以及对返回值的转换等等。这里面涉及的细节就很多了,包括HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler、RequestResponseBodyMethodProcessor、和HttpMessageConvert等组件。当
HandlerAdapter组件执行完成之后会得到一个ModleAndView组件,这个组件代表视图模型。得到
ModleAndView后会执行拦截器的postHandle方法。如果在上面的执行过程中发生任何异常,会由HandlerExceptionResolver进行统一处理。
最后模型解析器会对上面的到的
ModleAndView进行解析,得到一个一个View返回给客户端。在返回客户端之前还会执行拦截器的afterCompletion方法。
Spring MVC 处理一个请求的流程分析的更多相关文章
- spring mvc之启动过程源码分析
简介 这两个星期都在看spring mvc源码,看来看去还是还是很多细节没了解清楚,在这里把看明白的记录下,欢迎在评论中一起讨论. 一.铺垫 spring mvc是基于servlet的,在正式分析之前 ...
- Spring MVC的映射请求
一.SpringMVC常用注解 @Controller 声明Action组件 @Service 声明Service组件 @Service("myMovieLister" ...
- Spring MVC 异步处理请求,提高程序性能
原文:http://blog.csdn.net/he90227/article/details/52262163 什么是异步模式 如何在Spring MVC中使用异步提高性能? 一个普通 Servle ...
- spring mvc 文件下载 get请求解决中文乱码问题
方案简写,自己或有些基础的可以看懂,因为没时间写的那么详细 方案1 spring mvc解决get请求中文乱码问题, 在tamcat中server.xml文件 URIEncoding="UT ...
- Spring MVC中forward请求转发2种方式(带参数)
Spring MVC中forward请求转发2种方式(带参数) http://www.51gjie.com/javaweb/956.html
- Spring mvc 启动 和 请求分发
Spring mvc 启动 和 请求分发 启动加载: abstract class HttpServletBean extends HttpServlet void init() initServle ...
- Spring Boot与Spring MVC集成启动过程源码分析
开源项目推荐 Pepper Metrics是我与同事开发的一个开源工具(https://github.com/zrbcool/pepper-metrics),其通过收集jedis/mybatis/ht ...
- Spring MVC 原理介绍(执行流程)
Spring MVC工作流程图 图一 图二 Spring工作流程描述 1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServle ...
- Spring MVC如何获取请求中的参数
目录 一.获取URL中路径参数 1.1 @PathVariable 注解 1.2 @PathParam 注解 二.获取请求参数: 2.1 GET请求 2.1.1 获取请求中的单个参数:@Request ...
随机推荐
- DAS、SAN和NAS三种服务器存储方式 (转)
转 :https://blog.csdn.net/fgf00/article/details/52592651 2016年09月20日 09:04:00 凌_风 一.存储的分类根据服务器类型分为 ...
- Apache伪静态(Rewrite).htaccess文件详解
Htaccess(超文本访问)是一个简单的配置文件,它允许设计师,开发者和程序员通过它来改变Apache Web服务器的配置.这些功能包括用户重定向.URL重写(url rewrite,国内很多称为伪 ...
- php 二位数组 转一维数组
$result = []; array_map(function ($value) use (&$result) { $result = array_merge($result, array_ ...
- 使用Java语言编写一个五子棋UI界面并实现网络对战功能(非局域网)
使用Java语言编写一个五子棋UI界面并实现网络对战功能(非局域网) 一,前期准备 1,Java IDE(Eclipse)与JDK的安装与配置jdk-15.0.1-免配置路径版提取码:earu免安装版 ...
- 【MySQL 基础】MySQ LeetCode
MySQL LeetCode 175. 组合两个表 题目描述 表1: Person +-------------+---------+ | 列名 | 类型 | +-------------+----- ...
- Java JVM——9.方法区
前言 方法区是运行时数据区的最后一个部分: 从线程共享与否的角度来看: 大家可能在这里有些疑惑,方法区和元空间的关系到底是怎样的?请往下看,下面会为大家解惑. 栈.堆.方法区的交互关系 下面就涉及了对 ...
- Python作业---内置数据类型
实验2 内置数据类型 实验性质:验证性 一.实验目的 1.掌握内置函数.列表.切片.元组的基本操作: 2.掌握字典.集合和列表表达式的基本操作. 二.实验预备知识 1.掌握Python内置函数的基/本 ...
- git 遇到 fatal: loose object xxxx (stored in .git/objects/cb/xxxx) is corrupt 问题
我的git版本是2.3.x,用下面这个参考链接的方法也可以解决 参考blog
- cmd的终结工具cmder
常用快捷键 win+alt+t 打开任务设置窗口 win+alt+k 打开快捷键设置窗口 自定义屏幕分割窗口快捷键: ctl+shift+s 水平按50%比例分割 ctl+shift+v 垂直按50 ...
- commons-lang3相关类实例
一.ArrayUtils //1.判断两个数组长度是否相等 ArrayUtils.isSameLength(new int[] {1,2,3,4}, new int[] {1,2,3,4});//tr ...