ModeAndView

一、ModeAndView和DispatcherServlet

  • ModeAndView 是DispatcherServlet中请求处理器执行后的返回结果(HandlerAdapter 执行后的返回结果),在SpringMvc中ModeAndView是一个比较简单的类,没有继承关系,代码也比较少。
  • 下面是源码中对该类的注释
/**
 * 在MVC框架中同时持有Model和View,但是他们是相互独立的,这个类的目的仅仅是
 * 为了让Handler处理器能够在一起响应返回中同时返回MOdel和View
 *
 * Holder for both Model and View in the web MVC framework.
 * Note that these are entirely distinct. This class merely holds
 * both to make it possible for a controller to return both model
 * and view in a single return value.
 *
 * ModelAndView代表了一个请求处理器的返回, 由 DispatcherServlet 解析,
 * View 可以有一个名字,通过视图解析器 ViewResolver 来处理,也可以由一
 * 个名字来唯一确定,Model 是一个Map集合,可以使用多个键值对。
 *
 * <p>Represents a model and view returned by a handler, to be resolved
 * by a DispatcherServlet. The view can take the form of a String
 * view name which will need to be resolved by a ViewResolver object;
 * alternatively a View object can be specified directly. The model
 * is a Map, allowing the use of multiple objects keyed by name.
 */
  • 从前面的注释大概知道视图和 Model 是什么,Model 本质就是数据,可以理解为处理后返回的数据,比如键值对,View 由一个名字来标识,需要视图解析器来解析。

  • DispatcherServlet中关键代码如下关键代码如下:

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
	 
        //省略...
        ModelAndView mv = null;
		mappedHandler = getHandler(processedRequest);
		
		HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

		//调用Handler处理器,返回 ModeAndView
		mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
 
        //省略...
	}

二、代码

2.1 关键属性

public class ModelAndView {

	//View实例或者View的字符串名称
	@Nullable
	private Object view;

	//Model的Map集合
	@Nullable
	private ModelMap model;

	//响应的HTTP status选项
	@Nullable
	private HttpStatus status;

	//标记是否被clear方法清空
	private boolean cleared = false;

2.2 构造方法

  • ModeAndView提供了很多重载的构造方法,都比较简单,提供了不同的构造参数,列出典型的如下:
	public ModelAndView(String viewName, @Nullable Map<String, ?> model) {
		this.view = viewName;
		//将model属性添加到Mode对应的Map集合中
		if (model != null) {
			getModelMap().addAllAttributes(model);
		}
	}

2.3 读取方法

  • 读取方法主要是获取内部的 View 或者 Model,status 等属性,很多 setXX 方法;
    public String getViewName() {
		return (this.view instanceof String ? (String) this.view : null);
	}
	
	protected Map<String, Object> getModelInternal() {
		return this.model;
	}
	
	public ModelMap getModelMap() {
		if (this.model == null) {
			this.model = new ModelMap();
		}
		return this.model;
	}

2.4 赋值方法

  • 赋值方法主要是给属性赋值,也比较简单,都是 setXX 方法;
    public void setViewName(@Nullable String viewName) {
		this.view = viewName;
	}
	
	
	public void setStatus(@Nullable HttpStatus status) {
		this.status = status;
	}
	
	//添加模型值
	public ModelAndView addObject(String attributeName, @Nullable Object attributeValue) {
		getModelMap().addAttribute(attributeName, attributeValue);
		return this;
	}
	
	//清空
	public void clear() {
		this.view = null;
		this.model = null;
		this.cleared = true;
	}
  • ModeAndView 本身比较简单,内部持有一个 View 对象和其名称,还有一个 Model 对象,Model 本质是一个包含了属性的 Map 集合,提供了相关的读写方法,添加 Model 属性的方法等,后面我们看看保存 Model 属性的这个 ModelMap。

2.5 ModelMap

  • ModelMap 用于保存 Model 的属性,也比较简单,提供了很多属性的读写方法;
/**
 * 实现Map接口用于构建模型数据,支持链式调用和根据名称构建Model
 * 
 * Implementation of {@link java.util.Map} for use when building model data for use
 * with UI tools. Supports chained calls and generation of model attribute names.
 *
 * 这个类充当MVC的通用模型但是并不绑定。
 * 
 * <p>This class serves as generic model holder for Servlet MVC but is not tied to it.
 * Check out the {@link Model} interface for an interface variant.
 */
@SuppressWarnings("serial")
public class ModelMap extends LinkedHashMap<String, Object> {

    //构造方法
	public ModelMap(Object attributeValue) {
		addAttribute(attributeValue);
	}


    //添加键值对属性
	public ModelMap addAttribute(String attributeName, @Nullable Object attributeValue) {
		Assert.notNull(attributeName, "Model attribute name must not be null");
		put(attributeName, attributeValue);
		return this;
	}

	//添加单个属性
	public ModelMap addAttribute(Object attributeValue) {
		Assert.notNull(attributeValue, "Model object must not be null");
		if (attributeValue instanceof Collection && ((Collection<?>) attributeValue).isEmpty()) {
			return this;
		}
		return addAttribute(Conventions.getVariableName(attributeValue), attributeValue);
	}

	//添加属性
	public ModelMap addAllAttributes(@Nullable Map<String, ?> attributes) {
		if (attributes != null) {
			putAll(attributes);
		}
		return this;
	}

    //属性合并
	public ModelMap mergeAttributes(@Nullable Map<String, ?> attributes) {
		if (attributes != null) {
			attributes.forEach((key, value) -> {
				if (!containsKey(key)) {
					put(key, value);
				}
			});
		}
		return this;
	}

	//判断是否包含属性
	public boolean containsAttribute(String attributeName) {
		return containsKey(attributeName);
	}
}

三、View

  • View 代表 MVC 的视图,在 SpringMvc 中 View 是一个接口,其子类负责渲染内容并且呈现模型(数据)

  • 单个示图可以包含很多模型(数据);View的实现有很多种,典型的是JSP,其他实现比如 XSLT-based、Xml、html甚至Excel格式,定义接口的目的是避免限制可能的实现方式。

  • 示图是一个Bean,可以通过示图解析器ViewResolver来实例化,而且 View 是没有状态的,因此实现类是线程安全的。

  • 下面是View接口的源码,本身比较简单,省去了部分属性,只有两个方法;

public interface View {
 
	//返回示图的内容,可以在渲染之前检查示图的内容
	@Nullable
	default String getContentType() {
		return null;
	}

	 //按照指定的模型渲染视图,模式本质就是数据 
	 //第一步是准备请求,比如在 JSP 中就是设置模型数据为响应的属性
	 //第二步就是渲染视图,比如通过 RequestDispatcher 填充 JSP
	void render(@Nullable Map<String, ?> model, HttpServletRequest request,  HttpServletResponse response) throws Exception;
}
  • 后面文章再看看 View 接口的具体实现类,重点看看 MappingJackson2JsonView 实现类,并自定义实现一种 View 视图方式;
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐