Spring MVC 处理一个请求的流程
作者:程序员自由之路 发布时间:2021-12-19 18:39:39
一个请求从客户端发出到达服务器,然后被处理的整个过程其实是非常复杂的。本博客主要介绍请求到达服务器被核心组件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方法。
来源:https://www.cnblogs.com/54chensongxia/p/12525418.html


猜你喜欢
- 经过多次尝试之后,终于找到了开机自动启动App的解决方法开机后会停留在锁屏页面,且短时间内如果没有进行解锁操作,屏幕会进入休眠状态,所以启动
- SpringBoot-JPA删除不成功,只执行了查询语句今天使用JPA自定义了一个删除方法deleteByUserIdAndCommentI
- 主要注意的是在资源引用的地方AlertDialog.Builder(this,R.style.dialogNoBg).create();这里
- 一、简介前面介绍了平时用到的大多数的多线程的例子,但在实际开发中使用的线程往往是大量的和更为复杂的,这时,每次都创建线程、启动线程。从性能上
- C语言 strcmp() 函数用于对两个字符串进行比较(区分大小写)。头文件:string.h语法/原型:int strcmp(const
- 一、什么是内存泄漏内存泄漏是指你向系统申请分配内存进行使用(new/malloc),然后系统在堆内存中给这个对象申请一块内存空间,但当我们使
- 一、java final基本概念:1、主要用于修饰类、属性和方法:被final修饰的类不可以被继承被final修饰的方法不可以被重写被fin
- 一个项目中肯定会存在很多共用的查询数据,对于这一部分的数据,没必要每一个用户访问时都去查询数据库,因此配置二级缓存将是非常必要的。Mybat
- finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常
- MyBatis查询数据赋值给List集合数据缺少今天在使用MyBatis查询数据时,发现查出来的数据和List集合的大小不一致,如下图所示,
- Android 使用AsyncTask设置请求超时的注意事项final AsyncTaskTools task = new AsyncTas
- 今天被数据大神说了,对接第三方接口返回的json字段我想用驼峰形式,他说我这样不专业。所以就改了,认怂。记住以后再次对接rest接口,返回的
- 提示:这里咱们要说的常量池,常量池就是咱们面试中所说的常量池,谈谈你对常量池的认识?面试官一问咱们就懵逼了,你要记得你脑子中有一张图!!!
- 在C#winform程序开发过程中,我们可能需要定期去设定一些变化的值,但这些值在程序中又要被用来做对比或参照,比如我们设定一个固定值让程序
- 简介功能需求如图所示,点击下一个按钮,所有卡片向右滚动,其中最后一张需要变更为最前面的一张,点击上一个按钮,所有卡片向左滚动,最前面的一张需
- 前言不积跬步无以至千里,不积小流,无以成江海在公司一般来说,都只会接触一些CRUD的业务,很多时候可能你想设计很多的代码结构,但是时间不允许
- 流程分析首先,使用mybatis的时候会定义mapper接口的基础包,一般我们会用@MapperScanner这个注解,来看下这个注解&nb
- BufferedInputStream 介绍BufferedInputStream 是缓冲输入流。它继承于FilterInputStream
- 1.@RequestMapping注解1.1@RequestMapping注解的功能从注解名称上我们可以看到,@RequestMapping
- 本文以实例形式详细讲述了Java的反射机制,是Java程序设计中重要的技巧。分享给大家供大家参考。具体分析如下:首先,Reflection是