Spring Web : 概念模型 ModelAndViewContainer

概述

ModelAndViewContainerSpring Web提供的一个持有器工具,用于记录一个控制器方法调用过程中HandlerMethodArgumentResolver/HandlerMethodReturnValueHandler的有关决定。这些决定主要通过ModelAndViewContainer的如下实例属性来表示 :

  • requestHandled
    • 标记请求是否已经被处理完,如果请求已经被处理完,调用者无需再做后续处理工作:视图解析和渲染。
  • defaultModel
    • 缺省数据模型(一般指非跳转模式下的数据模型)
  • redirectModel
    • 针对跳转模式的数据模型
  • redirectModelScenario
    • 当前模式是否是跳转模式redirect
  • ignoreDefaultModelOnRedirect
    • 跳转模式下如果没有设置redirectModel 是否使用defaultModel
    • 缺省为false
  • view
    • 字符串视图名称
    • 或者视图对象自身
  • status
    • 将要写入到响应的状态字

源代码

源代码版本 : spring-webmvc-5.1.5.RELEASE

package org.springframework.web.method.support;

// 省略 import 行


public class ModelAndViewContainer {

    //  跳转模式下 , 如果没有设置 redirectModel (也就是 RedirectAttributes), 是否使用 defaultModel
    // 缺省值为false,不使用
	private boolean ignoreDefaultModelOnRedirect = false;

    // 最重要渲染的视图,可能是字符串表示视图名称,或是视图对象自身
	@Nullable
	private Object view;

    // 针对非跳转模式设计的数据模型
	private final ModelMap defaultModel = new BindingAwareModelMap();

    // 针对跳转模式设计的数据模型
	@Nullable
	private ModelMap redirectModel;

    // 标记当前模式是否为跳转模式
	private boolean redirectModelScenario = false;

    // 将要写入到响应的 HTTP 状态字
	@Nullable
	private HttpStatus status;

    // 记录通过注解 @ModelAttribute(binding=true/false) 设置为不要绑定的属性的名称
	private final Set<String> noBinding = new HashSet<>(4);

    // 用于记录程序化设置的禁止绑定的属性的集合
	private final Set<String> bindingDisabled = new HashSet<>(4);

    // 记录当前会话的状态,用于沟通协调会话处理
	private final SessionStatus sessionStatus = new SimpleSessionStatus();

    // 记录请求是否已经被完全处理,如果记录被完全处理,调用者就知道后续的处理不必要继续了
   // 初始值为 false
	private boolean requestHandled = false;


	/**
	 * By default the content of the "default" model is used both during
	 * rendering and redirect scenarios. Alternatively controller methods
	 * can declare an argument of type RedirectAttributes and use
	 * it to provide attributes to prepare the redirect URL.
	 * Setting this flag to true guarantees the "default" model is
	 * never used in a redirect scenario even if a RedirectAttributes argument
	 * is not declared. Setting it to false means the "default" model
	 * may be used in a redirect if the controller method doesn't declare a
	 * RedirectAttributes argument.
	 * The default setting is false.
	 */
	public void setIgnoreDefaultModelOnRedirect(boolean ignoreDefaultModelOnRedirect) {
		this.ignoreDefaultModelOnRedirect = ignoreDefaultModelOnRedirect;
	}

	/**
	 * Set a view name to be resolved by the DispatcherServlet via a ViewResolver.
	 * Will override any pre-existing view name or View.
	 */
	public void setViewName(@Nullable String viewName) {
		this.view = viewName;
	}

	/**
	 * Return the view name to be resolved by the DispatcherServlet via a
	 * ViewResolver, or null if a View object is set.
	 */
	@Nullable
	public String getViewName() {
		return (this.view instanceof String ? (String) this.view : null);
	}

	/**
	 * Set a View object to be used by the DispatcherServlet.
	 * Will override any pre-existing view name or View.
	 */
	public void setView(@Nullable Object view) {
		this.view = view;
	}

	/**
	 * Return the View object, or null if we using a view name
	 * to be resolved by the DispatcherServlet via a ViewResolver.
	 */
	@Nullable
	public Object getView() {
		return this.view;
	}

	/**
	 * Whether the view is a view reference specified via a name to be
	 * resolved by the DispatcherServlet via a ViewResolver.
	 */
	public boolean isViewReference() {
		return (this.view instanceof String);
	}

	/**
	 * Return the model to use -- either the "default" or the "redirect" model.
	 * The default model is used if redirectModelScenario=false or
	 * there is no redirect model (i.e. RedirectAttributes was not declared as
	 * a method argument) and ignoreDefaultModelOnRedirect=false.
	 */
	public ModelMap getModel() {
		if (useDefaultModel()) {
			return this.defaultModel;
		}
		else {
			if (this.redirectModel == null) {
				this.redirectModel = new ModelMap();
			}
			return this.redirectModel;
		}
	}

	/**
	 * Whether to use the default model or the redirect model.
	 */
	private boolean useDefaultModel() {
		return (!this.redirectModelScenario 
				|| (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
	}

	/**
	 * Return the "default" model created at instantiation.
	 * In general it is recommended to use #getModel() instead which
	 * returns either the "default" model (template rendering) or the "redirect"
	 * model (redirect URL preparation). Use of this method may be needed for
	 * advanced cases when access to the "default" model is needed regardless,
	 * e.g. to save model attributes specified via @SessionAttributes.
	 * @return the default model (never null)
	 * @since 4.1.4
	 */
	public ModelMap getDefaultModel() {
		return this.defaultModel;
	}

	/**
	 * Provide a separate model instance to use in a redirect scenario.
	 * The provided additional model however is not used unless
	 * #setRedirectModelScenario gets set to true
	 * to signal an actual redirect scenario.
	 */
	public void setRedirectModel(ModelMap redirectModel) {
		this.redirectModel = redirectModel;
	}

	/**
	 * Whether the controller has returned a redirect instruction, e.g. a
	 * "redirect:" prefixed view name, a RedirectView instance, etc.
	 */
	public void setRedirectModelScenario(boolean redirectModelScenario) {
		this.redirectModelScenario = redirectModelScenario;
	}

	/**
	 * Provide an HTTP status that will be passed on to with the
	 * ModelAndView used for view rendering purposes.
	 * @since 4.3
	 */
	public void setStatus(@Nullable HttpStatus status) {
		this.status = status;
	}

	/**
	 * Return the configured HTTP status, if any.
	 * @since 4.3
	 */
	@Nullable
	public HttpStatus getStatus() {
		return this.status;
	}

	/**
	 * Programmatically register an attribute for which data binding should not occur,
	 * not even for a subsequent @ModelAttribute declaration.
	 * @param attributeName the name of the attribute
	 * @since 4.3
	 */
	public void setBindingDisabled(String attributeName) {
		this.bindingDisabled.add(attributeName);
	}

	/**
	 * Whether binding is disabled for the given model attribute.
	 * @since 4.3
	 */
	public boolean isBindingDisabled(String name) {
		return (this.bindingDisabled.contains(name) || this.noBinding.contains(name));
	}

	/**
	 * Register whether data binding should occur for a corresponding model attribute,
	 * corresponding to an @ModelAttribute(binding=true/false) declaration.
	 * Note: While this flag will be taken into account by #isBindingDisabled,
	 * a hard #setBindingDisabled declaration will always override it.
	 * @param attributeName the name of the attribute
	 * @since 4.3.13
	 */
	public void setBinding(String attributeName, boolean enabled) {
		if (!enabled) {
			this.noBinding.add(attributeName);
		}
		else {
			this.noBinding.remove(attributeName);
		}
	}

	/**
	 * Return the SessionStatus instance to use that can be used to
	 * signal that session processing is complete.
	 */
	public SessionStatus getSessionStatus() {
		return this.sessionStatus;
	}

	/**
	 * Whether the request has been handled fully within the handler, e.g.
	 * @ResponseBody method, and therefore view resolution is not
	 * necessary. This flag can also be set when controller methods declare an
	 * argument of type ServletResponse or OutputStream).
	 * The default value is false.
     * 一个标志,标志请求是否已经在Handler内部被完全处理,比如使用了注解
     * @ResponseBody 的控制器方法就会被Handler完全处理,从而该标志会被
     * 设置成 true。另外如果控制器方法带有类型为 ServletResponse/OutputStream
     * 的参数,那么这个标志也会被设置为 true,因为这种情况下,框架会认为
     * 开发人员会自己处理响应结果的输出。
     * 缺省情况下,该标志为 false,也就是需要框架后续的视图解析和渲染过程。
	 */
	public void setRequestHandled(boolean requestHandled) {
		this.requestHandled = requestHandled;
	}

	/**
	 * Whether the request has been handled fully within the handler.
	 */
	public boolean isRequestHandled() {
		return this.requestHandled;
	}

	/**
	 * Add the supplied attribute to the underlying model.
	 * A shortcut for getModel().addAttribute(String, Object).
	 */
	public ModelAndViewContainer addAttribute(String name, @Nullable Object value) {
		getModel().addAttribute(name, value);
		return this;
	}

	/**
	 * Add the supplied attribute to the underlying model.
	 * A shortcut for getModel().addAttribute(Object).
	 */
	public ModelAndViewContainer addAttribute(Object value) {
		getModel().addAttribute(value);
		return this;
	}

	/**
	 * Copy all attributes to the underlying model.
	 * A shortcut for getModel().addAllAttributes(Map).
	 */
	public ModelAndViewContainer addAllAttributes(@Nullable Map<String, ?> attributes) {
		getModel().addAllAttributes(attributes);
		return this;
	}

	/**
	 * Copy attributes in the supplied Map with existing objects of
	 * the same name taking precedence (i.e. not getting replaced).
	 * A shortcut for getModel().mergeAttributes(Map<String, ?>).
	 */
	public ModelAndViewContainer mergeAttributes(@Nullable Map<String, ?> attributes) {
		getModel().mergeAttributes(attributes);
		return this;
	}

	/**
	 * Remove the given attributes from the model.
	 */
	public ModelAndViewContainer removeAttributes(@Nullable Map<String, ?> attributes) {
		if (attributes != null) {
			for (String key : attributes.keySet()) {
				getModel().remove(key);
			}
		}
		return this;
	}

	/**
	 * Whether the underlying model contains the given attribute name.
	 * A shortcut for getModel().containsAttribute(String).
	 */
	public boolean containsAttribute(String name) {
		return getModel().containsAttribute(name);
	}


	/**
	 * Return diagnostic information.
	 */
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder("ModelAndViewContainer: ");
		if (!isRequestHandled()) {
			if (isViewReference()) {
				sb.append("reference to view with name '").append(this.view).append("'");
			}
			else {
				sb.append("View is [").append(this.view).append(']');
			}
			if (useDefaultModel()) {
				sb.append("; default model ");
			}
			else {
				sb.append("; redirect model ");
			}
			sb.append(getModel());
		}
		else {
			sb.append("Request handled directly");
		}
		return sb.toString();
	}

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值