<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项工作。

  1. 得到当前线程的LocaleContext和RequestAttributes,创建新的LocaleContext和RequestAttributes并重新绑定到当前线程。

    调用子类实现的doService()

    重置当前线程的LocaleContext和RequestAttributes

    执行成功后,发布ServletRequestHandledEvent事件。

    2.DispatcherServlet自定义的doService方法

    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
    protectedvoiddoService(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>

    主要做两部分工作

    1. 如果是include请求,先保存一份request域数据的快照,doDispatch执行过后,将会用快照数据恢复。

      调用doDispatch方法,完成请求转发。

      3.doDispatch方法

      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
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      protectedvoiddoDispatch(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的请求处理流程的更多相关文章

  1. 2.SpringMVC源码分析:DispatcherServlet的初始化与请求转发

    一.DispatcherServlet的初始化 在我们第一次学Servlet编程,学java web的时候,还没有那么多框架.我们开发一个简单的功能要做的事情很简单,就是继承HttpServlet,根 ...

  2. springmvc源码分析系列-请求处理流程

    接上一篇-springmvc源码分析开头片 上一节主要说了一下springmvc与struts2的作为MVC中的C(controller)控制层的一些区别及两者在作为控制层方面的一些优缺点.今天就结合 ...

  3. springMVC源码分析--DispatcherServlet请求获取及处理

    在之前的博客springMVC源码分析--容器初始化(二)DispatcherServlet中我们介绍过DispatcherServlet,是在容器初始化过程中出现的,我们之前也说过Dispatche ...

  4. SpringMVC源码分析--容器初始化(五)DispatcherServlet

    上一篇博客SpringMVC源码分析--容器初始化(四)FrameworkServlet我们已经了解到了SpringMVC容器的初始化,SpringMVC对容器初始化后会进行一系列的其他属性的初始化操 ...

  5. springMVC源码分析--容器初始化(二)DispatcherServlet

    在上一篇博客springMVC源码分析--容器初始化(一)中我们介绍了spring web初始化IOC容器的过程,springMVC作为spring项目中的子项目,其可以和spring web容器很好 ...

  6. 8、SpringMVC源码分析(3):分析ModelAndView的形成过程

    首先,我们还是从DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response) throw ...

  7. SpringMVC源码情操陶冶-DispatcherServlet类简析(一)

    阅读源码有利于陶冶情操,此文承接前文SpringMVC源码情操陶冶-DispatcherServlet父类简析 注意:springmvc初始化其他内容,其对应的配置文件已被加载至beanFactory ...

  8. 框架-springmvc源码分析(一)

    框架-springmvc源码分析(一) 参考: http://www.cnblogs.com/heavenyes/p/3905844.html#a1 https://www.cnblogs.com/B ...

  9. [心得体会]SpringMVC源码分析

    1. SpringMVC (1) springmvc 是什么? 前端控制器, 主要控制前端请求分配请求任务到service层获取数据后反馈到springmvc的view层进行包装返回给tomcat, ...

随机推荐

  1. Java中终止线程的三种方法

    终止线程一般建议采用的方法是让线程自行结束,进入Dead(死亡)状态,就是执行完run()方法.即如果想要停止一个线程的执行,就要提供某种方式让线程能够自动结束run()方法的执行.比如设置一个标志来 ...

  2. Linux下启动停止查看杀死Tomcat进程

    文章来自:http://www.linuxidc.com/Linux/2011-06/37180.htm 启动 一般是执行tomcat/bin/startup.sh,sh tomcat/bin/sta ...

  3. 13个开源GIS软件 你了解几个?

    地理信息系统(Geographic Information System,GIS)软件依赖于覆盖整个地球的数据集.为处理大量的 GIS 数据及其格式,编程人员创建了若干开源库和 GIS 套件. GIS ...

  4. linux_磁盘挂载

    mount -o loop 磁盘的位置 想要挂载的位置 磁盘卸载 umont 挂载的磁盘的详细位置 注意:磁盘卸载时你当前所在的路径不要在磁盘挂载的路径,应该其他与磁盘挂载路径不相干的路径下即可

  5. mysql 初始密码、修改密码

    新装MySQL,进不去,找不到网上说的什么临时密码,也没有见到放临时密码的文件,历经坎坷,终解决,,在此记录,谨防下次忘记,在此感谢原作者博文 系统 Ubuntu18.04 mysql Ver 14. ...

  6. 819. Most Common Word

    static int wing=[]() { std::ios::sync_with_stdio(false); cin.tie(NULL); ; }(); class Solution { publ ...

  7. 744. Find Smallest Letter Greater Than Target

    俩方法都是用二分查找,一个调库,一个自己写而已. 方法一,调库 static int wing=[]() { std::ios::sync_with_stdio(false); cin.tie(NUL ...

  8. 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[ ...

  9. js监听微信、支付宝返回,后退、上一页按钮事件

    $(function(){ pushHistory(); window.addEventListener("popstate", function(e) { alert(" ...

  10. SQL错误

    一.mybatis框架XML错误 1.ORA-00918: 未明确定义列:SQL语句中列明重复,或者定义不明确(关联查询时两张表都有要区分开列明) 2.无效的列类型: 1111  :a.传入数据漏传一 ...