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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...