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

2022-07-19 06:02:16

在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