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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC源码解析 - HandlerAdater - ModelAndViewContainer上下文容器相关的知识,希望对你有一定的参考价值。

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 }

 

以上是关于SpringMVC源码解析 - HandlerAdater - ModelAndViewContainer上下文容器的主要内容,如果未能解决你的问题,请参考以下文章

SpringMVC源码分析6:SpringMVC的视图解析原理

源码深度解析SpringMvc请求运行机制

SpringMVC源码解析- HandlerAdapter - ModelFactory

SpringMVC源码解析 - HandlerAdapter - @SessionAttributes注解处理

SpringMVC源码解析- HandlerAdapter初始化

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