SpringMVC源碼解析 - HandlerAdater - ModelAndViewContainer上下文容器

来源:http://www.cnblogs.com/leftthen/archive/2016/02/28/5222753.html
-Advertisement-
Play Games

HandlerAdapter在處理請求時上下文數據的傳遞工作是由ModelAndViewContainer負責的. 就是記錄HandlerMethodArgumentResolver 和 HandlerMethodReturnValueHandler 在處理handler時 使用的模型model和視...


HandlerAdapter在處理請求時上下文數據的傳遞工作是由ModelAndViewContainer負責的.

源碼註釋是這樣描述的:

Records model and view related decisions made by HandlerMethodArgumentResolvers and HandlerMethodReturnValueHandlers during the course of invocation of a controller method.

翻譯下: 記錄HandlerMethodArgumentResolver 和 HandlerMethodReturnValueHandler 在處理handler時 使用的模型model和視圖view相關信息.

ModelAndViewContainer主要職責:

  1. 維護模型model,包括defaultModle和redirectModel

  2. 維護視圖view

  3. 維護是否redirect信息,及根據這個判斷HandlerAdapter使用的是defaultModel或redirectModel(判斷規則詳見下文)

  4. 維護@SessionAttributes註解信息狀態

  5. 維護handler是否處理標記

 

目錄:

1. ModelAndViewContainer屬性

2. 科普ModelMap繼承體系

3. ModelAndView提供的api,複雜的還是model相關的屬性設置,其他主要是簡單的getter,setter

 

ModelAndViewContainer屬性

先來看看ModelAndViewContainer的屬性,這樣就比較清晰:

 1 package org.springframework.web.method.support;
 2 public class ModelAndViewContainer {
 3     // 視圖,實際使用時可能是String類型的邏輯視圖
 4     private Object view;
 5     // 標記handler是否已經完成請求處理
 6     private boolean requestHandled = false;
 7     // 預設模型,下文我們可以簡單科普下ModelMap繼承體系
 8     private final ModelMap defaultModel = new BindingAwareModelMap();
 9     // redirect時使用的模型,實際使用的是RedirectAttributesModelMap
10     private ModelMap redirectModel;
11     // 標記處理器返回redirect視圖
12     private boolean redirectModelScenario = false;
13     // redirect時,是否忽略defaultModel
14     private boolean ignoreDefaultModelOnRedirect = false;
15     // @SessionAttributes註解使用狀態標記,就是是否處理完畢
16     private final SessionStatus sessionStatus = new SimpleSessionStatus();
17 }

 順便學個英語單詞,Scenario 方案

 

 

科普ModelMap繼承體系

繼續往下分析之前,我們先來簡單科普下ModelMap的繼承體系:

各個類的職責與使用場景:

  ModelMap是LinkedHashMap的子類,主要是封裝attribute概念,實際處理還是委托給map

  ExtendedModelMap添加鏈調用chained calls,並實現Model介面

  BindingAwareModelMap,BindingResult相關屬性被設置時,自動清除BindingResult.defaultModel使用的該類.

  RedirectAttributesModelMap 通過DataBinder做參數類型轉換,redirect時使用的flash attributes

 

各有側重地看下源碼吧

  2.1 ModelMap,是繼承LinkedHashMap,添加mergeAttributes 

 1 package org.springframework.ui;
 2 public class ModelMap extends LinkedHashMap<String, Object> {
 3     public ModelMap mergeAttributes(Map<String, ?> attributes) {
 4         if (attributes != null) {
 5             for (String key : attributes.keySet()) {
 6                 if (!containsKey(key)) {
 7                     put(key, attributes.get(key));
 8                 }
 9             }
10         }
11         return this;
12     }
13     // ...
14 }

  2.2 ExtendedModelMap添加鏈調用chained calls

1 package org.springframework.ui;
2 public class ExtendedModelMap extends ModelMap implements Model {
3     @Override
4     public ExtendedModelMap addAttribute(String attributeName, Object attributeValue) {
5         super.addAttribute(attributeName, attributeValue);
6         return this;// 看這裡
7     }
8     // ...
9 }

  2.3 BindingAwareModelMap,BindingResult相關屬性被設置時,自動清除BindingResult.defaultModel使用的該類.

  看的就是removeBindingResultIfNecessary.這邊put和putAll都會調用removeBindingResultIfNecessary

 1 package org.springframework.validation.support;
 2 public class BindingAwareModelMap extends ExtendedModelMap {
 3 
 4     @Override
 5     public Object put(String key, Object value) {
 6         removeBindingResultIfNecessary(key, value);
 7         return super.put(key, value);
 8     }
 9     // ...
10     private void removeBindingResultIfNecessary(Object key, Object value) {
11         if (key instanceof String) {
12             String attributeName = (String) key;
13             if (!attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
14                 String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attributeName;
15                 BindingResult bindingResult = (BindingResult) get(bindingResultKey);
16                 if (bindingResult != null && bindingResult.getTarget() != value) {
17                     remove(bindingResultKey);
18                 }
19             }
20         }
21     }
22 
23 }

  2.4 RedirectAttributesModelMap 通過DataBinder做參數類型轉換,redirect時使用的flash attributes

  這樣主要就是添加了dataBinder和flashAttributes屬性相關的api

 1 package org.springframework.web.servlet.mvc.support;
 2 public class RedirectAttributesModelMap extends ModelMap implements RedirectAttributes {
 3 
 4     private final DataBinder dataBinder;
 5 
 6     private final ModelMap flashAttributes = new ModelMap();
 7 
 8     private String formatValue(Object value) {
 9         if (value == null) {
10             return null;
11         }
12         return (dataBinder != null) ? dataBinder.convertIfNecessary(value, String.class) : value.toString();
13     }
14     // ...
15 }

 

ModelAndView提供的api

主要是兩類api,view相關和model相關.

  3.1 view相關其實就一個api,是否類應用(通過是否是string類型判斷):

 1 package org.springframework.web.method.support;
 2 public class ModelAndViewContainer {
 3     // ...
 4     /**
 5      * Whether the view is a view reference specified via a name to be
 6      * resolved by the DispatcherServlet via a ViewResolver.
 7      */
 8     public boolean isViewReference() {
 9         return (this.view instanceof String);
10     }
11 }

  3.2 model相關的api就比較多了,主要是設置模型值.

  在設置模型值的時候,這邊涉及到一個問題,就是container里有兩個model,往哪個裡設置?

  container索性抽象一個getModel()的api進行判斷.

  我們來說說判斷的邏輯吧:

    使用defaultModel的場景:

    a,不是redirect  

    b,redirect場景下,redirectModel為null,同時ignoreDefaultModelOnRedirect為false

    剩下的就是使用redirectModel的場景了,不細說

  model相關屬性操作的類都是跟addAttribute類似的邏輯,使用getModel獲取model後直接委托.所以篇幅緣故下麵的源碼以addAttribute為例,其他都只是展現api 的signature

 1 package org.springframework.web.method.support;
 2 public class ModelAndViewContainer {
 3     // ...
 4     /**
 5      * Return the model to use: the "default" or the "redirect" model.
 6      * <p>The default model is used if {@code "redirectModelScenario=false"} or
 7      * if the redirect model is {@code null} (i.e. it wasn't declared as a
 8      * method argument) and {@code ignoreDefaultModelOnRedirect=false}.
 9      */
10     public ModelMap getModel() {
11         if (useDefaultModel()) {
12             return this.defaultModel;
13         }
14         else {
15             return (this.redirectModel != null) ? this.redirectModel : new ModelMap();
16         }
17     }
18     /**
19      * Whether to use the default model or the redirect model.
20      * 不是redirect || redirect時redirectModel為空同時不忽略defaultModel
21      */
22     private boolean useDefaultModel() {
23         return !this.redirectModelScenario || ((this.redirectModel == null) && !this.ignoreDefaultModelOnRedirect);
24     }
25     public ModelAndViewContainer addAttribute(String name, Object value) {
26         getModel().addAttribute(name, value);
27         return this;
28     }
29     public ModelAndViewContainer addAttribute(Object value){};
30     public ModelAndViewContainer addAllAttributes(Map<String, ?> attributes){};
31     public ModelAndViewContainer removeAttributes(Map<String, ?> attributes){};
32     public boolean containsAttribute(String name){};
33 }

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • spring沒有採用約定優於配置的策略,spring要求顯示指定搜索哪些路徑下的Java文件。spring將會把合適的java類全部註冊成spring Bean。 問題:spring怎麼知道把哪些Java類當初bean類處理? 這就需要使用annotation,spring使用一些特殊的annota
  • 本文將通過一個 Python 實現的圖片文件批量重命名工具來闡述如何逐步提升程式質量。
  • 學了線程,收穫不少,記錄下了吧. 一、線程的主要兩種實現方法。 1.繼承Thread類,重寫run()方法 main方法中創建子類,引用調用start()方法 實例如下: //繼承Thread類,重寫run()方法 public class ThreadOne extends Thread { pu
  • 《數據結構》第2章第10節歸併擴展的線性鏈表。
  • BlockingQueue簡介 ArrayBlockingQueue:基於數組實現的一個阻塞隊列,在創建ArrayBlockingQueue對象時必須制定容量大小。並且可以指定公平性與非公平性,預設情況下為非公平的,即不保證等待時間最長的隊列最優先能夠訪問隊列。 LinkedBlockingQueu
  • 1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<ctype.h> 4 5 #define OK 1 6 #define ERROR 0 7 #define STACK_INIT_SIZE 20 8 #define STACK_INCREMENT
  • 1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<math.h> 4 5 #define INIT_STACK_SZIE 20 6 #define STACK_INCREMENT 10 7 #define OK 1 8 #define ERROR
  • 1 #include<stdio.h> 2 #include<stdlib.h> 3 4 #define STACK_INIT_SIZE 10 5 #define STACKINCREASE 10 6 #define OK 1 7 #define ERROR 0 8 9 typedef int El
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...