ModelAndViewContainer 组件分析
ModelAndViewContainer 承担着整个请求过程中数据的传递工作;
处理保存 Model 和 View 之外,还有一些其他功能
1,ModelAndViewContain 中的变量
// 若为true,处理器返回redirect属兔是一定不使用defaultModelprivate boolean ignoreDefaultModelOnRedirect = false;// 视图,Object类型,View类型的实际视图或String类型的逻辑视图private Object view;// 默认使用的Modelprivate final ModelMap defaultModel = new BindingAwareModelMap();// redirect类型的Modelprivate ModelMap redirectModel;// 处理器返回redirect类型视图的标记private boolean redirectModelScenario = false;
/* Names of attributes with binding disabled */private final Set<String> bindingDisabledAttributes = new HashSet<String>(4);
private HttpStatus status;// 用于设置SessionAttribute使用完的标志private final SessionStatus sessionStatus = new SimpleSessionStatus();// 请求是否已经处理完成的标志private boolean requestHandled = false;
复制代码
2,defaultModel 和 redirectModel
在 ModelAndViewContain 中,有两个 Model:
defaultModel:默认使用的 Model;
redirectModel:用于传递 redirect 时的 Model;
当处理器中,使用了 Model 或 ModelMap 时,ArgumentResolve 会传入 defaultModel;
defaultModel 是 BindingAwareModelMap 类型,继承了 ModelMap 同时又实现了 Model 接口;
所以在处理器中,使用 Model 或 ModelMap 实际使用的是同一个对象,Map 参数传入的也是这个对象;
处理器中 RedirectAttributes 类型的参数 ArgumentResolve 会传入 redirectModel;
redirectModel:实际是 RedirectAttributesModelMap 类型;
3,getModel
ModelAndViewContain#getModeModelAndViewContain#getModel 方法会根据条件返回这两个 Model 中的一个
public ModelMap getModel() { if (useDefaultModel()) { return this.defaultModel; } else { if (this.redirectModel == null) { this.redirectModel = new ModelMap(); } return this.redirectModel; }}
private boolean useDefaultModel() { return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));}
复制代码
useDefaultModel 方法逻辑:
ignoreDefaultModelOnRedirect 可在 RequestMappingHandlerAdapter 设置,
redirectModelScenario 是处理器返回是否为 redirect 视图的标志,在 ReturnValueHandler 中设置,
ReturnValueHandler 如果判断出是 redirect 视图,会将 redirectModelScenario 置为 true;
也就是说,在 ReturnValueHandler 处理前的 ModelAndViewContain 的 Model 一定是 defaultModel,只有在 ReturnValueHandler 处理之后才有可能是 redirectModel;
4,getModelAndView
了解 getModel 的逻辑,再回去看 RequestMappingHandlerAdapter#getModelAndView 中的 getModel:
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer); if (mavContainer.isRequestHandled()) { return null; } ModelMap model = mavContainer.getModel(); ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } return mav;}
复制代码
getModel返回redirectModel时,处理器设置到Model的参数不会被使用(设置了SessionAttribute的除外)只有redirect的情况才会返回redirectModel,而这种情况不需要渲染页面,所以defaultModel中的参数没什么用
getModel返回defaultModel时,设置到RedirectAttributes中的参数将被丢弃,所以,返回的View不是redircet类型时,即使处理器使用RedirectAttributes设置了参数也不会被传递到下一个请求
通过@SessionAttribute传递的参数是在ModelFactory#updateModel中设置的使用了mavContainer.getDefaultModel方法,确保任何情况下使用的都是defaultModel所以,只有将参数设置到Model或ModelMap中才能使用sessionAttribute缓存,设置到RedirectAttributes中的参数不可以
复制代码
public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception { // 取defaultModel, // 设置到Model或ModelMap中才能使用sessionAttribute缓存 // 设置到RedirectAttributes中的参数不可以 ModelMap defaultModel = container.getDefaultModel(); if (container.getSessionStatus().isComplete()){ this.sessionAttributesHandler.cleanupAttributes(request); } else { this.sessionAttributesHandler.storeAttributes(request, defaultModel); } if (!container.isRequestHandled() && container.getModel() == defaultModel) { updateBindingResult(request, defaultModel); }}
复制代码
5,ModelAndViewContainer
属性操作 ModelAndViewContainer 提供了添加、合并、删除属性的方法,这些方法都是直接调用 Model 操作的:
// 添加key-valuepublic ModelAndViewContainer addAttribute(String name, Object value) { getModel().addAttribute(name, value); return this;}// 添加对象public ModelAndViewContainer addAttribute(Object value) { getModel().addAttribute(value); return this;}// 批量添加public ModelAndViewContainer addAllAttributes(Map<String, ?> attributes) { getModel().addAllAttributes(attributes); return this;}// 合并:如果原Model中不包含的传入属性则添加进去public ModelAndViewContainer mergeAttributes(Map<String, ?> attributes) { getModel().mergeAttributes(attributes); return this;}// 批量删除public ModelAndViewContainer removeAttributes(Map<String, ?> attributes) { if (attributes != null) { for (String key : attributes.keySet()) { getModel().remove(key); } } return this;}
复制代码
6,sessionStatus 属性
sessionStatus 属性
是处理器中通知SessionAttribute使用完成时所用的SessionStatus类型的参数sessionStatus用于标示SessionAttribute是否已经使用完如果使用完了,则在ModelFactory#updateModel方法中将SessionAttribute对应参数清除否则将当前Model的相应参数设置进去
复制代码
7,requestHandler 属性
requestHandler 属性
用于标示请求是否已经全部处理完毕如果处理完毕就不再往下处理,直接返回全部处理完毕:指已经返回response如处理器返回值有@ResponseBody注释或返回值为HttpEntity类型等,都会将requestHandler置为true
复制代码
评论