SpringMVC底層——請求參數處理流程描述

在DispatcherServlet.java的doDispatch方法中,springmvc通過handlermapping裡面找哪個handler能處理請求,handler封裝了目標方法的資訊,

mappedHandler = getHandler(processedRequest);

然後為當前的handler找到一個適配器HandlerAdapter,尋找的過程為:在DispatcherServlet.java的getHandlerAdapter方法中,挨個匹配,判斷當前adapter是否支援當前handler,判斷方法為只要handler是handlerMethod類型就生效,就支援

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
if (this.handlerAdapters != null) {
	for (HandlerAdapter adapter : this.handlerAdapters) {
		if (adapter.supports(handler)) {
			return adapter;
			}
	}
}

找到適配器以後判斷當前請求是不是「GET」方法以及「HEAD」,「HEAD」不是伺服器真正處理的

適配器HandlerAdapter把(目標方法、request、response)傳入handle執行目標方法

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

怎麼執行目標方法:

  • 先得到handler
return this.handler;
  • 再進入內部處理細節RequestMappingHandlerAdapter.java,調用的invokeHandlerMethod就是執行目標方法
mav = invokeHandlerMethod(request,response,handlerMethod);
  • 在RequestMappingHandlerAdapter.java的invokeHandlerMethod方法中,

    • 為invocableMethod方法設置參數解析器argumentResolvers,參數解析器確定將要執行的目標方法的每一個參數的值是什麼

      • 當前解析器是否支援解析這種參數

      • 支援就調用 resolveArgument

    • 為invocableMethod方法設置返回值處理器returnValueHandlers

  • 把26個argumentResolvers和15個returnValueHandlers都放入目標方法包裝的ServletInvocableHandlerMethod中

  • 然後真正執行目標方法的語句

    invocableMethod.invokeAndHandle(WebRequest,mavContainer);
    
    • 在ServletInvocableHandlerMethod.java的invokeAndHandle方法中,執行了controller

      Object returnValue = invokeForRequest(webRequest,mavContainer,proviedArgs);
      
      • step into 進入InvocableHandlerMethod.java,確定目標方法每一個參數的值

        Object[] agrs = getMethodArgumentValues(request,mavContainer,providerArgs)
        
        • 在InvocableHandlerMethod.java的getMethodArgumentValues方法中,先獲取方法所有的參數聲明(詳細資訊)。

          MethodParameter[] parameters = getMethodParameters();
          
        • 判斷參數是否為空,為空則無需確定任何值直接返回;

          if (ObjectUtils.isEmpty(parameters)) {
              return EMPTY_ARGS;
          }
          

          如果有參數列表,new一個Object[],參數列表有多少個Object[]就有多長

          Object[] args = new Object[parameters.length];
          	for (int i = 0; i < parameters.length; i++) {
          		MethodParameter parameter = parameters[i];
          		parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
          		args[i] = findProvidedArgument(parameter, providedArgs);
          

          先聲明args遍歷parameters,給args[i]賦值,args[i]的值解析器解析了才有

        • 解析之前,判斷26個解析器是不是supportsParamter支援這個參數類型。

          this.resolvers.supportsParameter(parameter)
          
          • HandlerMethodArgumentResolverComposite.java的getArgumentResolver方法中

          • 獲取一個快取result

          • result==null,進入增強for循環,逐個確定26個解析器誰能支援這種參數

            • supportsParameter方法,傳來的參數有沒有hasParameterAnnotation標註註解
            • 沒有就return false
            • 如果標了,再判斷參數是否map類型,
            • return true支援解析
          • 當前resolver支援解析,放到快取裡邊,判斷成功,進入解析

        • 解析參數

          this.resolvers.resolveArgument
          
          • HandlerMethodArgumentResolverComposite.java的resolverArgument方法中,先拿到所有的參數解析器getArgumentResolver
          • 調用參數解析器的resolverArgument方法進行解析
            • 獲取參數名字資訊
            • 解析參數的名字,placeholderResolved、BeanExpressionResolver解析evaluate計算名字,按照正則匹配的方式
            • 解析參數的值
              • uriTemlateVars 在request請求域中拿到值;UrlPathHelper會把uri地址裡邊的所有的路徑變數全部解析出來並保存到請求域中
        • 遍歷循環所有參數

      • 最終返回args,args就是確定好的值

    • 處理返回結果的時候,把mavContainer傳進去,

      this.returnValueHandlers.handlerReturnValue
      
      • 在handlerMethod.java的getReturnValueType方法中獲取返回的結果類型

      • HandlerMethodArgumentResolverComposite.java的handleReturnValue方法中,

        找到返回值的處理器

        如果返回值是一個字元串,拿到字元串然後保存到mavContainer

    • 返回值處理完以後,getModelAndView

  • 目標方法執行完成

    將所有的數據都放在 ModelAndViewContainer;包含要去的頁面地址View。還包含Model數據。

  • 從ModelAndViewContainer拿到默認的Model,updateBindingResult,拿到key放到綁定裡邊,又被封裝成ModelAndView,然後返回這個新封裝的mav

    • 處理派髮結果

      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
      

      拿到所有請求域中的屬性,解析得到視圖名

      渲染頁面

      view.render(mv.getModelInternal(),request,response);
      

      拿到頁面數據

      createMergeOutputModel(model,request,response);
      創造合併的輸出模型
      

      如果model不等於空

      mergeModel.putAll(model)//即把數據轉移到HashMap
      

      渲染合併輸出的模型數據

      renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
      
      • 把上面的Hashmap傳進來了

      • 拿到請求對象,獲取的原生的Servletrequest

        return originalRequest
        
      • 暴露模型作為請求域屬性

        // Expose the model object as request attributes.
        		exposeModelAsRequestAttributes(model, request);
        
        • 把model裡面的東西進行遍歷
        • 遍歷以後每一個request setAttribute
Tags: