07-ModeAndView和View

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 視圖方式;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章