SpringMVC源码分析(3)DispatcherServlet的请求处理流程
<springmvc源码分析(2)dispatcherservlet的初始化>初始化DispatcherServlet的多个组件。
本文继续分析DispatcherServlet解析请求的过程。
概览
①:DispatcherServlet是springmvc中的前端控制器(front controller),负责接收request并将request转发给对应的处理组件.
②:HanlerMapping是springmvc中完成url到controller映射的组件.DispatcherServlet接收request,然后从HandlerMapping查找处理request的controller.
③:Cntroller处理request,并返回ModelAndView对象,Controller是springmvc中负责处理request的组件(类似于struts2中的Action),ModelAndView是封装结果视图的组件.
④ ⑤ ⑥:视图解析器解析ModelAndView对象并返回对应的视图给客户端.
要点
维护url和controller的映射
这部分工作由DefaultAnnotationHandlerMapping.setApplicationContext的父类
org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping.initApplicationContext实现。具体方法为detectHandlers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
protectedvoiddetectHandlers()throwsBeansException{ if (logger.isDebugEnabled()){ logger.debug( "LookingforURLmappingsinapplicationcontext:" +getApplicationContext()); } String[]beanNames=( this .detectHandlersInAncestorContexts? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(),Object. class ): getApplicationContext().getBeanNamesForType(Object. class )); //TakeanybeannamethatwecandetermineURLsfor. for (StringbeanName:beanNames){ String[]urls=determineUrlsForHandler(beanName); if (!ObjectUtils.isEmpty(urls)){ //URLpathsfound:Let'sconsideritahandler. registerHandler(urls,beanName); } else { if (logger.isDebugEnabled()){ logger.debug( "Rejectedbeanname'" +beanName+ "':noURLpathsidentified" ); } } } } |
2.准确定位处理请求的具体方法(在AnnotationMethodHandlerAdapter中实现)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
protectedModelAndViewinvokeHandlerMethod(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler) throwsException{ ServletHandlerMethodResolvermethodResolver=getMethodResolver(handler); MethodhandlerMethod=methodResolver.resolveHandlerMethod(request); //具体实现方法的匹配 ServletHandlerMethodInvokermethodInvoker=newServletHandlerMethodInvoker(methodResolver); ServletWebRequestwebRequest=newServletWebRequest(request,response); ExtendedModelMapimplicitModel=newBindingAwareModelMap(); Objectresult=methodInvoker.invokeHandlerMethod(handlerMethod,handler,webRequest,implicitModel); ModelAndViewmav= methodInvoker.getModelAndView(handlerMethod,handler.getClass(),result,implicitModel,webRequest); methodInvoker.updateModelAttributes(handler,(mav!= null ?mav.getModel(): null ),implicitModel,webRequest); returnmav; } |
1.请求入口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
@Override protectedfinalvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse) throwsServletException,IOException{ processRequest(request,response); } /** *DelegatePOSTrequeststo{@link#processRequest}. *@see#doService */ @Override protectedfinalvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse) throwsServletException,IOException{ processRequest(request,response); } protectedfinalvoidprocessRequest(HttpServletRequestrequest,HttpServletResponseresponse) throwsServletException,IOException{ longstartTime=System.currentTimeMillis(); ThrowablefailureCause= null ; //ExposecurrentLocaleResolverandrequestasLocaleContext. LocaleContextpreviousLocaleContext=LocaleContextHolder.getLocaleContext(); LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this .threadContextInheritable); //ExposecurrentRequestAttributestocurrentthread. RequestAttributespreviousRequestAttributes=RequestContextHolder.getRequestAttributes(); ServletRequestAttributesrequestAttributes= null ; if (previousRequestAttributes== null ||previousRequestAttributes.getClass().equals(ServletRequestAttributes. class )){ requestAttributes=newServletRequestAttributes(request); RequestContextHolder.setRequestAttributes(requestAttributes, this .threadContextInheritable); } if (logger.isTraceEnabled()){ logger.trace( "Boundrequestcontexttothread:" +request); } try { doService(request,response); } catch (ServletExceptionex){ failureCause=ex; throwex; } catch (IOExceptionex){ failureCause=ex; throwex; } catch (Throwableex){ failureCause=ex; thrownewNestedServletException( "Requestprocessingfailed" ,ex); } finally { //Clearrequestattributesandresetthread-boundcontext. LocaleContextHolder.setLocaleContext(previousLocaleContext, this .threadContextInheritable); if (requestAttributes!= null ){ RequestContextHolder.setRequestAttributes(previousRequestAttributes, this .threadContextInheritable); requestAttributes.requestCompleted(); } if (logger.isTraceEnabled()){ logger.trace( "Clearedthread-boundrequestcontext:" +request); } if (failureCause!= null ){ this .logger.debug( "Couldnotcompleterequest" ,failureCause); } else { this .logger.debug( "Successfullycompletedrequest" ); } if ( this .publishEvents){ //Whetherornotwesucceeded,publishanevent. longprocessingTime=System.currentTimeMillis()-startTime; this .webApplicationContext.publishEvent( newServletRequestHandledEvent( this , request.getRequestURI(),request.getRemoteAddr(), request.getMethod(),getServletConfig().getServletName(), WebUtils.getSessionId(request),getUsernameForRequest(request), processingTime,failureCause)); } } } |
processRequest方法主要做4项工作。
得到当前线程的LocaleContext和RequestAttributes,创建新的LocaleContext和RequestAttributes并重新绑定到当前线程。
调用子类实现的doService()
重置当前线程的LocaleContext和RequestAttributes
执行成功后,发布ServletRequestHandledEvent事件。
2.DispatcherServlet自定义的doService方法
1234567891011121314151617181920212223242526272829303132333435363738protectedvoiddoService(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{
if
(logger.isDebugEnabled()){
StringrequestUri=urlPathHelper.getRequestUri(request);
logger.debug(
"DispatcherServletwithname'"
+getServletName()+
"'processing"
+request.getMethod()+
"requestfor["
+requestUri+
"]"
);
}
//Keepasnapshotoftherequestattributesincaseofaninclude,
//tobeabletorestoretheoriginalattributesaftertheinclude.
Map<string,object>attributesSnapshot=
null
;
if
(WebUtils.isIncludeRequest(request)){
logger.debug(
"Takingsnapshotofrequestattributesbeforeinclude"
);
attributesSnapshot=newHashMap<string,object>();
EnumerationattrNames=request.getAttributeNames();
while
(attrNames.hasMoreElements()){
StringattrName=(String)attrNames.nextElement();
if
(
this
.cleanupAfterInclude||attrName.startsWith(
"org.springframework.web.servlet"
)){
attributesSnapshot.put(attrName,request.getAttribute(attrName));
}
}
}
//Makeframeworkobjectsavailabletohandlersandviewobjects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE,getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE,
this
.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE,
this
.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE,getThemeSource());
try
{
doDispatch(request,response);
}
finally
{
//Restoretheoriginalattributesnapshot,incaseofaninclude.
if
(attributesSnapshot!=
null
){
restoreAttributesAfterInclude(request,attributesSnapshot);
}
}
}</string,object></string,object>
主要做两部分工作
如果是include请求,先保存一份request域数据的快照,doDispatch执行过后,将会用快照数据恢复。
调用doDispatch方法,完成请求转发。
3.doDispatch方法
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697protectedvoiddoDispatch(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{
HttpServletRequestprocessedRequest=request;
HandlerExecutionChainmappedHandler=
null
;
intinterceptorIndex=-
1
;
try
{
ModelAndViewmv;
booleanerrorView=
false
;
try
{
//1.检查是否是文件上传的请求
processedRequest=checkMultipart(request);
//Determinehandlerforthecurrentrequest.
//2.取得处理当前请求的controller,这里也称为hanlder,处理器,第一个步骤的意义就在这里体现了.
//这里并不是直接返回controller,而是返回的HandlerExecutionChain请求处理器链对象,该对象封装了handler和interceptors.
mappedHandler=getHandler(processedRequest,
false
);
if
(mappedHandler==
null
||mappedHandler.getHandler()==
null
){
noHandlerFound(processedRequest,response);
return
;
}
//Determinehandleradapterforthecurrentrequest.
//3.获取处理request的处理器适配器handleradapter
HandlerAdapterha=getHandlerAdapter(mappedHandler.getHandler());
//Processlast-modifiedheader,ifsupportedbythehandler.
Stringmethod=request.getMethod();
booleanisGet=
"GET"
.equals(method);
if
(isGet||
"HEAD"
.equals(method)){
longlastModified=ha.getLastModified(request,mappedHandler.getHandler());
if
(logger.isDebugEnabled()){
StringrequestUri=urlPathHelper.getRequestUri(request);
logger.debug(
"Last-Modifiedvaluefor["
+requestUri+
"]is:"
+lastModified);
}
if
(newServletWebRequest(request,response).checkNotModified(lastModified)&&isGet){
return
;
}
}
//ApplypreHandlemethodsofregisteredinterceptors.
//4.拦截器的预处理方法
HandlerInterceptor[]interceptors=mappedHandler.getInterceptors();
if
(interceptors!=
null
){
for
(inti=
0
;i<interceptors.length;i++){ actuallyinvokethehandler.=
""
applyposthandlemethodsofregisteredinterceptors.=
""
handlerinterceptorinterceptor=
"interceptors[i];"
interceptorindex=
"i;"
inti=
"interceptors.length-1;i"
mv=
"ha.handle(processedRequest,response,mappedHandler.getHandler());"
>=
0
;i--){
HandlerInterceptorinterceptor=interceptors[i];
interceptor.postHandle(processedRequest,response,mappedHandler.getHandler(),mv);
}
}
}
catch
(ModelAndViewDefiningExceptionex){
logger.debug(
"ModelAndViewDefiningExceptionencountered"
,ex);
mv=ex.getModelAndView();
}
catch
(Exceptionex){
Objecthandler=(mappedHandler!=
null
?mappedHandler.getHandler():
null
);
mv=processHandlerException(processedRequest,response,handler,ex);
errorView=(mv!=
null
);
}
//Didthehandlerreturnaviewtorender?
if
(mv!=
null
&&!mv.wasCleared()){
render(mv,processedRequest,response);
if
(errorView){
WebUtils.clearErrorRequestAttributes(request);
}
}
else
{
if
(logger.isDebugEnabled()){
logger.debug(
"NullModelAndViewreturnedtoDispatcherServletwithname'"
+getServletName()+
"':assumingHandlerAdaptercompletedrequesthandling"
);
}
}
//Triggerafter-completionforsuccessfuloutcome.
triggerAfterCompletion(mappedHandler,interceptorIndex,processedRequest,response,
null
);
}
catch
(Exceptionex){
//Triggerafter-completionforthrownexception.
triggerAfterCompletion(mappedHandler,interceptorIndex,processedRequest,response,ex);
throwex;
}
catch
(Errorerr){
ServletExceptionex=newNestedServletException(
"Handlerprocessingfailed"
,err);
//Triggerafter-completionforthrownexception.
triggerAfterCompletion(mappedHandler,interceptorIndex,processedRequest,response,ex);
throwex;
}
finally
{
//Cleanupanyresourcesusedbyamultipartrequest.
if
(processedRequest!=request){
cleanupMultipart(processedRequest);
}
}
}</interceptors.length;i++){>
很明显这儿是SpringMVC核心。
1.根据请求的路径找到HandlerMethod(带有Method反射属性,也就是对应Controller中的方法)(DispatcherServlet.getHandler完成)
2.匹配路径对应的拦截器(DispatcherServlet.getHandler完成)
3.获得HandlerExecutionChain对象(DispatcherServlet.getHandler完成)
4.通过HandlerAdapter对象进行处理得到ModelAndView对象(HandlerAdapter.handle)
5.调用HandlerInterceptor.preHandle
6.调用HandlerInterceptor.postHandle
7. 渲染
4.总结
简单粗暴的总结下
step1-6: 获取controller
step5-15 :调用controller方法
step17-20:渲染view
其他:aop方式处理拦截统一处理。
SpringMVC源码分析(3)DispatcherServlet的请求处理流程的更多相关文章
- 2.SpringMVC源码分析:DispatcherServlet的初始化与请求转发
一.DispatcherServlet的初始化 在我们第一次学Servlet编程,学java web的时候,还没有那么多框架.我们开发一个简单的功能要做的事情很简单,就是继承HttpServlet,根 ...
- springmvc源码分析系列-请求处理流程
接上一篇-springmvc源码分析开头片 上一节主要说了一下springmvc与struts2的作为MVC中的C(controller)控制层的一些区别及两者在作为控制层方面的一些优缺点.今天就结合 ...
- springMVC源码分析--DispatcherServlet请求获取及处理
在之前的博客springMVC源码分析--容器初始化(二)DispatcherServlet中我们介绍过DispatcherServlet,是在容器初始化过程中出现的,我们之前也说过Dispatche ...
- SpringMVC源码分析--容器初始化(五)DispatcherServlet
上一篇博客SpringMVC源码分析--容器初始化(四)FrameworkServlet我们已经了解到了SpringMVC容器的初始化,SpringMVC对容器初始化后会进行一系列的其他属性的初始化操 ...
- springMVC源码分析--容器初始化(二)DispatcherServlet
在上一篇博客springMVC源码分析--容器初始化(一)中我们介绍了spring web初始化IOC容器的过程,springMVC作为spring项目中的子项目,其可以和spring web容器很好 ...
- 8、SpringMVC源码分析(3):分析ModelAndView的形成过程
首先,我们还是从DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response) throw ...
- SpringMVC源码情操陶冶-DispatcherServlet类简析(一)
阅读源码有利于陶冶情操,此文承接前文SpringMVC源码情操陶冶-DispatcherServlet父类简析 注意:springmvc初始化其他内容,其对应的配置文件已被加载至beanFactory ...
- 框架-springmvc源码分析(一)
框架-springmvc源码分析(一) 参考: http://www.cnblogs.com/heavenyes/p/3905844.html#a1 https://www.cnblogs.com/B ...
- [心得体会]SpringMVC源码分析
1. SpringMVC (1) springmvc 是什么? 前端控制器, 主要控制前端请求分配请求任务到service层获取数据后反馈到springmvc的view层进行包装返回给tomcat, ...
随机推荐
- Java中终止线程的三种方法
终止线程一般建议采用的方法是让线程自行结束,进入Dead(死亡)状态,就是执行完run()方法.即如果想要停止一个线程的执行,就要提供某种方式让线程能够自动结束run()方法的执行.比如设置一个标志来 ...
- Linux下启动停止查看杀死Tomcat进程
文章来自:http://www.linuxidc.com/Linux/2011-06/37180.htm 启动 一般是执行tomcat/bin/startup.sh,sh tomcat/bin/sta ...
- 13个开源GIS软件 你了解几个?
地理信息系统(Geographic Information System,GIS)软件依赖于覆盖整个地球的数据集.为处理大量的 GIS 数据及其格式,编程人员创建了若干开源库和 GIS 套件. GIS ...
- linux_磁盘挂载
mount -o loop 磁盘的位置 想要挂载的位置 磁盘卸载 umont 挂载的磁盘的详细位置 注意:磁盘卸载时你当前所在的路径不要在磁盘挂载的路径,应该其他与磁盘挂载路径不相干的路径下即可
- mysql 初始密码、修改密码
新装MySQL,进不去,找不到网上说的什么临时密码,也没有见到放临时密码的文件,历经坎坷,终解决,,在此记录,谨防下次忘记,在此感谢原作者博文 系统 Ubuntu18.04 mysql Ver 14. ...
- 819. Most Common Word
static int wing=[]() { std::ios::sync_with_stdio(false); cin.tie(NULL); ; }(); class Solution { publ ...
- 744. Find Smallest Letter Greater Than Target
俩方法都是用二分查找,一个调库,一个自己写而已. 方法一,调库 static int wing=[]() { std::ios::sync_with_stdio(false); cin.tie(NUL ...
- 2018.10.24 NOIP模拟 小 C 的序列(链表+数论)
传送门 考虑到a[l],gcd(a[l],a[l+1]),gcd(a[l],a[l+1],a[l+2])....gcd(a[l]...a[r])a[l],gcd(a[l],a[l+1]),gcd(a[ ...
- js监听微信、支付宝返回,后退、上一页按钮事件
$(function(){ pushHistory(); window.addEventListener("popstate", function(e) { alert(" ...
- SQL错误
一.mybatis框架XML错误 1.ORA-00918: 未明确定义列:SQL语句中列明重复,或者定义不明确(关联查询时两张表都有要区分开列明) 2.无效的列类型: 1111 :a.传入数据漏传一 ...