Spring MVC 处理一个请求的流程

 更新时间:2021年02月05日 14:24:10   作者:程序员自由之路  
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 处理请求的资料请关注脚本之家其它相关文章!

相关文章

  • 如何处理maven仓库中后缀LastUpdated文件

    如何处理maven仓库中后缀LastUpdated文件

    这篇文章主要介绍了如何处理maven仓库中后缀LastUpdated文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • hibernate批量操作实例详解

    hibernate批量操作实例详解

    这篇文章主要介绍了hibernate批量操作,结合实例形式分析了Hibernate实现批量插入,更新及删除等操作的具体实现技巧,需要的朋友可以参考下
    2016-03-03
  • Restful传递数组参数及注解大全

    Restful传递数组参数及注解大全

    这篇文章主要介绍了Restful传递数组参数及注解大全的相关资料,需要的朋友可以参考下
    2015-12-12
  • Java基于二维数组实现的数独问题示例

    Java基于二维数组实现的数独问题示例

    这篇文章主要介绍了Java基于二维数组实现的数独问题,涉及java针对数组的遍历、计算、转换等相关操作技巧,需要的朋友可以参考下
    2018-01-01
  • Java HashMap算法原理详细讲解

    Java HashMap算法原理详细讲解

    在java开发中,HashMap是最常用、最常见的集合容器类之一,文中通过示例代码介绍HashMap为啥要二次Hash,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-02-02
  • Java 详解如何从尾到头打印链表

    Java 详解如何从尾到头打印链表

    在我们平时的代码过程中,链表是我们经常遇到的一个数据结构,它非常的简单,但Java并不能直接将一个链表打印出来,通过这篇文章我们来讲解一下这个问题
    2022-01-01
  • 基于Java在netty中实现线程和CPU绑定

    基于Java在netty中实现线程和CPU绑定

    这篇文章主要介绍了基于Java在netty中实现线程和CPU绑定,文章围绕主题的相关内容展开详细介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-05-05
  • Java源码解析之ClassLoader

    Java源码解析之ClassLoader

    在看系统启动的流程中看到了ClassLoader使用,重新温故下ClassLoader流程和原理,文中有非常详细的代码示例,对正在学习java的小伙伴们很有帮助,需要的朋友可以参考下
    2021-05-05
  • Java设计模式之动态代理

    Java设计模式之动态代理

    今天小编就为大家分享一篇关于Java设计模式之动态代理,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • SpringBoot无法识别bootstrap.yml小绿叶问题的解决办法

    SpringBoot无法识别bootstrap.yml小绿叶问题的解决办法

    一般单独使用 Spring Boot 时,bootstrap.yml 文件一般是不会生效的,也就是没有小绿叶图标,本文给大家介绍了SpringBoot无法识别bootstrap.yml小绿叶问题的解决办法,文中给出了两种解决方案,需要的朋友可以参考下
    2024-07-07

最新评论