SpringMVC源碼分析(3)DispatcherServlet的請求處理流程


<springmvc源碼分析(2)dispatcherservlet的初始化>初始化DispatcherServlet的多個組件。

本文繼續分析DispatcherServlet解析請求的過程。

概覽

231546366285136.jpg

  ①: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.總結

      wKioL1g-153C8LZpAACyG_bLg7c573.png

      簡單粗暴的總結下

      step1-6: 獲取controller

      step5-15 :調用controller方法

      step17-20:渲染view

      其他:aop方式處理攔截統一處理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM