文章目錄
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 視圖方式;