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 ...
随机推荐
- NIO基础操作
原文链接http://zhhll.icu/2020/05/18/java%E5%9F%BA%E7%A1%80/IO/NIO%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/ N ...
- linux脚本错误: line *: [: missing `]',linux编写shell脚本时的注意点
转载:https://www.cnblogs.com/bovenson/p/4548079.html 关于shell的一些注意点,粘贴自拉钩教育精选评论:测试开发核心技术 46 讲-->第6讲 ...
- FastApi学习(一)
前言 学习不止 正文 介绍 FastApi是PythonWeb框架的'新晋干员',虽然年轻但是很能打 目前已有 12k start GitHub 官网 为什么说他能打呢?它内部使用了 Python 的 ...
- 【剑指 Offer】12.矩阵中的路径
题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左.右.上.下移动一格. 如果一条路径经过了矩阵的某一格,那么 ...
- AI智能皮肤测试仪助力美业数字化营销 实现门店与用户双赢局面
当皮肤遇到AI智能,会有怎么样的火花呢?随着生活水平的提升,人们对肌肤保养护理的需求也越来越高,人要美,皮肤养护也要更精准,数字化必将成为美业发展的新契机.新机遇下肌肤管家SkinRun为美业客户提供 ...
- 浅谈sql索引
索引是什么 假如你手上有一个你公司的客户表,老板说找什么客户你就得帮他找出来. 客户不多的时候,你拿着手指一行一行滑,费不了多少时间就能找到. 后来公司做大了,客户越来越多,好几页的客户,你发现,一行 ...
- win10/windows 安装Pytorch
https://pytorch.org/get-started/locally/ 去官网,选择你需要的版本. 把 pip install torch==1.5.0+cu101 torchvision= ...
- 【Linux】linux rinetd 端口转发部署
linux下简单好用的工具rinetd,实现端口映射/转发/重定向 Rinetd是为在一个Unix和Linux操作系统中为重定向传输控制协议(TCP)连接的一个工具.Rinetd是单一过程的服务器,它 ...
- 【VNC】vnc安装oracle的时候不显示图形化界面
背景: 在虚拟机搭建了一个环境,准备安装oracle.但是环境都配置完成后,执行./runInstaller的时候,没有界面显示,只显示下面的界面 多次尝试后,发现,还是这样,期初是因为没有配置DIS ...
- Nginx和Tomcat配置SSL实现https访问
环境:CentOS 7 Nginx版本: nginx/1.18.0 1. 安装nginx 详细步骤可以参考如下官网:http://nginx.org/en/linux_packages.html#RH ...