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 }