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 处理请求的资料请关注其它相关文章!
上一篇: 你爸喜欢铁观音
下一篇: ruby实现石头剪刀布游戏示例
推荐阅读
-
Spring MVC源码(二) ----- DispatcherServlet 请求处理流程 面试必问
-
图解 Spring:HTTP 请求的处理流程与机制【1】
-
图解 Spring:HTTP 请求的处理流程与机制【5】
-
记录spring-cloud-gateway获取post请求body参数,以及后端服务处理后的响应参数过程 gateway:2.2.0.RELEASE
-
ASP.NET/MVC/Core的HTTP请求流程
-
spring boot请求异常处理并返回对应的html页面
-
Spring MVC中异常处理的三种方式
-
HandlerMethodArgumentResolver(三):基于消息转换器的参数处理器【享学Spring MVC】
-
spring mvc 对ajax的处理
-
一个老程序员是如何手写Spring MVC的?