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

weixin_34104341發表於2020-04-07

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 }

 

轉載於:https://www.cnblogs.com/leftthen/p/5222753.html

相關文章