Spring mvc原理解析

1.@RequestMapping

  1. @RequestMapping註解來映射URl,返回值會通過視圖解析器解析爲實際的物理試圖,對於org.springframework.web.servlet.view.InternalResourceViewResolver試圖通過prefix+returnVal+後綴這樣的方式得到實際的物理試圖,然後做轉發操作.
<bean class=*"org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name=􏰙*prefix*􏰧 *value=*􏰙*/WEB-INF/view/"></property>
  <property name=*"suffix" value=".jsp"></property>
</bean>

2.@ModelAttribute

被@ModelAttribute標記的方法,會在每一個目標方法執行之前被springmvc調用

    @ModelAttribute
    public void getUserInfo(@RequestParam(value = "id",required = false) Integer id, Map<String,Object> map){
         System.out.println("xxxxxx");
        if(id!=null){
            Person person=new Person("x","1");
            System.out.println(person);
            map.put("person",person);
        }
    }


    @RequestMapping("/demo/xx")
    public ModelAndView demo(HttpServletRequest request, HttpServletResponse response,Person person ){
        System.out.println("x");
        ModelAndView modelAndView=new ModelAndView();
        modelAndView.addObject(person);
        return modelAndView;
    }

運行流程:

  1. 執行@ModelAttribute註解修飾的方法,從數據庫中取出對象,把對象放到map中,
  2. springmvc從map取出User對象,並把表單的請求參數賦值到給該對象的對象屬性
  3. springmvc吧上述對象傳入目標方法參數

源碼分析

  1. 首先會調用@ModelAttribute 註解修飾的方法, 實際上把@ModelAttribute方法中的map 的數據保存到implicitModel中
  2. 解析請求參數中的目標參數,實際上該目標參數 來自於WebDateBinder對象的target屬性
    WebDateBinder=bingObject(target對象)+attrName
    1. 創建WebDateBinder對象
      • 確定objectName屬性:若傳入的attrNa me屬性值爲"",則objectName爲類型首字母小寫,如果目標方法的pojo屬性使用了@ModelAttribute來修飾,則attrName值爲@ModelAttribute的value屬性值
      • 確定target屬性
        1. 在implicitModel中查找attrName對應的屬性值,若存在,OK
        2. 若不存在則驗證當前handler是否使用了@SessionAttributes進行修飾,若使用了,則嘗試從session中獲取attrName所對應的屬性值,若session中沒有對應的屬性值,則拋出異常
        3. 若Handler沒有使用@SessionAttributes修飾,或@SessionAttributes中沒有使用value值指定的key和attrName相匹配,則通過反射創建POJO對象
    2. springmvc把表單的請求參數賦給了WebDateBinder的target對象
    3. springmvc會把WebDateBinder的attrName和target給到implicitModel,傳到request域中
    4. 把WebDateBinder的target作爲參數傳遞給目標方法入參

3.springmvc是怎麼確定目標對象方法POJO類型入參的過程的?

  1. 確定一個key
    • 若目標方法的pojo類型的參數沒有@ModelAttribute作爲修飾,則key爲POJO類型的首字母小寫
    • 若使用了@ModelAttribute來修飾,則key爲@ModelAttribute註解的value屬性值
  2. 在implicitModel中查找key對象的對象,若存在,則作爲入參傳入
    1. 若在@ModelAttribute標記的方法中在map中保存在,且key和之前確定的key一致,則會獲取到
  3. 若implicitModel獲取不到key對應的對象,則會檢查當前的handler是否使用@SessionAttributes註解修飾,若使用了該註解,且@SessionAttributes註解的value包含了key,則會從HttpSession獲取到key所對應的value的值,若存在則直接傳入到目標對象的入參中,若不存在則將拋出異常
  4. 若handler沒有標識@SessionAttributes註解或@SessionAttributes註解的@Value不包含key ,則通過反射創建POJO類型的參數,傳入目標方法的參數
  5. springmvc會把key和POJO類型的對象保存到implicitModel中,進而保存到request中

4.springmvc數據綁定流程

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-iLt64TnN-1575211185165)(/Users/laiyanxin/Library/Application Support/typora-user-images/image-20191201140712842.png)]

流程

  1. Springmvc通過反射機制對目標處理方法進行解析,並將請求消息綁定到處理方法入參中.

  2. Springmvc會將ServletRequest對象及目標處理方法的參數對象實例傳遞到DataBinder,DataBinder調用裝配在Spring web上下文的ConversionService組件進行數據類型轉換,數據格式化工作,並將ServletRequest中的消息填充到參數對象中,然後再調用Vaildator組件已經綁定了請求消息數據的參數對象進行數據合法性校驗,並最終生成數據綁定結果BindingResult對象.

  3. BindingResult對象包含了已完成數據綁定參數對象,還包含了相對應的校驗錯誤對象.

5.@RequestBody @ResponseBody原理

工作原理圖[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-TPrzMpcm-1575211185166)(/Users/laiyanxin/Library/Application Support/typora-user-images/image-20191201153135250.png)]

  1. 從請求報文先轉爲HttpInputMessage再由HttpMessgetConverter轉爲我們需要的java對象
  2. spingmvc返回一個對象先轉爲 HttpMessgetConverter再轉HttpOutPutMessage再響應信息
public interface HttpInputMessage extends HttpMessage {
 //把請求信息轉爲輸入流
	InputStream getBody() throws IOException;
}
public interface HttpOutputMessage extends HttpMessage {
  //把請求信息轉爲輸出流
	OutputStream getBody() throws IOException;
}

核心在於HttpMessgetConverter,SpringMVC處理請求和響應時,支持多種類型的請求參數和返回類型,而此種功能的實現就需要對HTTP消息體和參數及返回值進行轉換,爲此SpringMVC提供了大量的轉換類,所有轉換類都實現了HttpMessageConverter接口。

6攔截器執行流程

攔截器有三個方法

  1. preHandle 在目標方法之前調用
  2. postHandle 調用目標方法後,渲染試圖之前
  3. afterCompletion 渲染試圖後
  4. 攔截器可以通過配置作用的路徑<mvc:mapping ><mvc:exclude-mapping>配置包含關係
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   boolean multipartRequestParsed = false;

   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

   try {
      ModelAndView mv = null;
      Exception dispatchException = null;

     ----省略代碼-----
				//在目標方法之前調用
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // 調用目標方法
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }

         applyDefaultViewName(processedRequest, mv);
     			//調用目標方法後,渲染試圖之前調用
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      //渲染試圖
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
  
}
//渲染視圖
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		boolean errorView = false;

		if (exception != null) {
			------....---
		}

		if (mappedHandler != null) {
			// 渲染視圖完之後調用
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

7攔截器執行順序

  1. 正常流程

第一個攔截器preHandle—》第二個攔截器preHandle–》目標方法–》第二個攔截器postHandle–》第一個攔截器postHandle --》第二個攔截器afterCompletion–》第一個攔截器afterCompletion

  1. 第二個攔截器preHandle返回false

第一個攔截器preHandle—》第二個攔截器preHandle–》第一個攔截器afterCompletion

  1. preHandle正序,afterCompletion和postHandle倒序

8. springmvc執行流程圖

在這裏插入圖片描述
9.springmvc源碼解析

核心方法在於DispatcherServlet中的doDispatch

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   boolean multipartRequestParsed = false;

   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

   try {
      ModelAndView mv = null;
      Exception dispatchException = null;

      try {
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // 獲取當前請求的HandlerExecutionChain,根據handleMapping獲取
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }

         //獲取.HandlerAdapter
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

         // Process last-modified header, if supported by the handler.
         String method = request.getMethod();
         boolean isGet = "GET".equals(method);
         if (isGet || "HEAD".equals(method)) {
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }

         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // 調用目標方法
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }

         applyDefaultViewName(processedRequest, mv);
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
         dispatchException = ex;
      }
      catch (Throwable err) {
        
         dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   catch (Throwable err) {
      triggerAfterCompletion(processedRequest, response, mappedHandler,
            new NestedServletException("Handler processing failed", err));
   }
   finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
         // Instead of postHandle and afterCompletion
         if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
         }
      }
      else {
         // Clean up any resources used by a multipart request.
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}
  1. 先獲取一個mappingHandler,類型就是HandleExecutionChain,處理器調用鏈(包含了攔截器,目標方法和handler ),通過handlerMapping獲取

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
       if (this.handlerMappings != null) {
          for (HandlerMapping mapping : this.handlerMappings) {
             HandlerExecutionChain handler = mapping.getHandler(request);
             if (handler != null) {
                return handler;
             }
          }
       }
       return null;
    }
    
  2. 獲取到hadlerAdapter,( 包含messageConver,數據轉換,數據格式化,校驗等等)

  3. 調用目標方法返回ModelAndView

  4. 調用攔截的applyPostHandle

  5. 處理視圖processDispatchResult

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
      @Nullable Exception exception) throws Exception {

   boolean errorView = false;
  //如果有異常就通過handlerExceptionResolvers來處理
   if (exception != null) {
      if (exception instanceof ModelAndViewDefiningException) {
         logger.debug("ModelAndViewDefiningException encountered", exception);
         mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      }
      else {
         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
         mv = processHandlerException(request, response, handler, exception);
         errorView = (mv != null);
      }
   }

   // 如果沒異常就進行渲染試圖
   if (mv != null && !mv.wasCleared()) {
      render(mv, request, response);
   }

   if (mappedHandler != null) {
      // 渲染完調用 攔截器afterCompletion
      mappedHandler.triggerAfterCompletion(request, response, null);
   }
}
  • 會先判斷是否有異常,如果有異常就通過handlerExceptionResolvers來處理

  • 如果沒異常就進行渲染試圖

    • 會先通過ViewResolvers得到View
    • 下一步渲染視圖
  • 渲染完調用 攔截器afterCompletion

  • 會先判斷是否有異常,如果有異常就通過handlerExceptionResolvers來處理

  • 如果沒異常就進行渲染試圖

    • 會先通過ViewResolvers得到View
    • 下一步渲染視圖
  • 渲染完調用 攔截器afterCompletion

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章