一个请求从客户端发出到达服务器,然后被处理的整个过程其实是非常复杂的。本博客主要介绍请求到达服务器被核心组件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 处理请求的资料请关注www.887551.com其它相关文章!