SpringMVC源码分析-HandlerAdapter(4)-ModelAndViewContain组件分析

ModelAndViewContainer组件分析

ModelAndViewContainer承担着整个请求过程中数据的传递工作
处理保存Model和View之外,还有一些其他功能

ModelAndViewContain中的变量

// 若为true,处理器返回redirect属兔是一定不使用defaultModel
private boolean ignoreDefaultModelOnRedirect = false;
// 视图,Object类型,View类型的实际视图或String类型的逻辑视图
private Object view;
// 默认使用的Model
private final ModelMap defaultModel = new BindingAwareModelMap();
// redirect类型的Model
private 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;

defaultModel和redirectModel:

ModelAndViewContain中有两个Model: defaultModel和redirectModel
defaultModel是默认使用的Model
redirectModel是用于传递redirect时的Model

处理器中使用了Model或ModelMap时,ArgumentResolve会传入defaultModel
defaultModel是BindingAwareModelMap类型,继承了ModelMap又实现了Model接口
所以在处理器中使用Model或ModelMap实际使用的是同一个对象,Map参数传入的也是这个对象

处理器中RedirectAttributes类型的参数,ArgumentResolve会传入redirectModel
redirectModel实际是RedirectAttributesModelMap类型

ModelAndViewContain#getMode

ModelAndViewContain#getMode方法会根据条件返回这两个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方法逻辑:

    如果redirectModelScenario为false,即返回的不是redirect视图时,
    一定返回defaultModel

    如果redirectModelScenario为true,如果返回redirect视图,
    redirectModel不为空,或ignoreDefaultModelOnRedirect=true返回redirectModel


ignoreDefaultModelOnRedirect可在RequestMappingHandlerAdapter设置
redirectModelScenario是处理器返回是否为redirect视图的标志,在ReturnValueHandler中设置,
ReturnValueHandler如果判断出是redirect视图会将redirectModelScenario置为true

也就是说,在ReturnValueHandler处理前的ModelAndViewContain的给Model一定是defaultModel
只有在ReturnValueHandler处理之后才有可能是redirectModel

了解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);
    }
}

属性操作

ModelAndViewContainer提供了添加,合并,删除属性的方法
这些方法都是直接调用Model操作的

// 添加key-value
public 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;
}

sessionStatus属性

sessionStatus属性

是处理器中通知SessionAttribute使用完成时所用的SessionStatus类型的参数
sessionStatus用于标示SessionAttribute是否已经使用完
如果使用完了,则在ModelFactory#updateModel方法中将SessionAttribute对应参数清除
否则将当前Model的相应参数设置进去

requestHandler属性

requestHandler属性

用于标示请求是否已经全部处理完毕
如果处理完毕就不再往下处理,直接返回
全部处理完毕:指已经返回response
如处理器返回值有@ResponseBody注释或返回值为HttpEntity类型等,都会将requestHandler置为true

猜你喜欢

转载自blog.csdn.net/abap_brave/article/details/80958107