欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring MVC 处理一个请求的流程

程序员文章站 2022-03-29 22:53:25
一个请求从客户端发出到达服务器,然后被处理的整个过程其实是非常复杂的。本博客主要介绍请求到达服务器被核心组件dispatcherservlet处理的整理流程(不包括filter的处理流程)。1. 处理...

一个请求从客户端发出到达服务器,然后被处理的整个过程其实是非常复杂的。本博客主要介绍请求到达服务器被核心组件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. 请求流程图

Spring MVC 处理一个请求的流程

还是这个图比较清楚。发现根据代码不太能把这个流程说清楚。而且整个流程很长,代码很多,我就不贴代码了。这里根据这个图再把整个流程中组件的功能总结下:

  • 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 处理请求的资料请关注其它相关文章!