SpringMVC基礎——返回值問題

来源:http://www.cnblogs.com/solverpeng/archive/2016/08/06/5738314.html
-Advertisement-
Play Games

一、SpringMVC 使用 ModelAndView 來處理返回值問題。 1.ModelAndView 官方描述: Holder for both Model and View in the web MVC framework.Note that these are entirely distin ...


一、SpringMVC 使用 ModelAndView 來處理返回值問題。

1.ModelAndView

官方描述:

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.

<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.

 

 

 

 

 

 

 

 

說明一下:

在 springmvc 框架中,ModelAndView 表示同時持有 Model 和 View 。Model 和 View 是完全不同的。

這個類使一個控制器返回單獨的一個值同時包含 model 和 view 成為一種可能。

同時也代表著一個處理器返回一個 model 和 view ,會被 DispatcherServlet 解析。

View 對象如果是通過一個字元串形式的 view name 獲取到的,則能被 ViewResolver 對象解析。

或者也可以直接指定 View 對象。model 是一個 Map 類型,可以使用多個對象的鍵值對。

2.這裡要搞明白一點,為什麼說是通過 ModelAndView 來處理返回值問題,事實上,返回的類型可以是很多種,可以是 ModelAndView 類型,

也可以是String類型,還可以是View類型,還有很多。但是他們最終會被解析為 ModelAndView 類型的對象。

通過源碼來證實一下:

org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#invokeHandlerMethod

在這個方法中:

Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);

其中 result 就是我們調用 handler 方法後的返回值。

通過 mehtodInvoker.getModelAndView() 方法將 result 最終解析為 ModelAndView 對象。

來看看具體過程:

public ModelAndView getModelAndView(Method handlerMethod, Class<?> handlerType, Object returnValue,
                ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {
    ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
    if (responseStatusAnn != null) {
        HttpStatus responseStatus = responseStatusAnn.value();
        String reason = responseStatusAnn.reason();
        if (!StringUtils.hasText(reason)) {
            webRequest.getResponse().setStatus(responseStatus.value());
        }
        else {
            webRequest.getResponse().sendError(responseStatus.value(), reason);
        }

        // to be picked up by the RedirectView
        webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus);

        responseArgumentUsed = true;
    }

    // Invoke custom resolvers if present...
    if (customModelAndViewResolvers != null) {
        for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {
            ModelAndView mav = mavResolver.resolveModelAndView(
                    handlerMethod, handlerType, returnValue, implicitModel, webRequest);
            if (mav != ModelAndViewResolver.UNRESOLVED) {
                return mav;
            }
        }
    }

    if (returnValue instanceof HttpEntity) {
        handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest);
        return null;
    }
    else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
        handleResponseBody(returnValue, webRequest);
        return null;
    }
    else if (returnValue instanceof ModelAndView) {
        ModelAndView mav = (ModelAndView) returnValue;
        mav.getModelMap().mergeAttributes(implicitModel);
        return mav;
    }
    else if (returnValue instanceof Model) {
        return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
    }
    else if (returnValue instanceof View) {
        return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
    }
    else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
        addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
        return new ModelAndView().addAllObjects(implicitModel);
    }
    else if (returnValue instanceof Map) {
        return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map<String, ?>) returnValue);
    }
    else if (returnValue instanceof String) {
        return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
    }
    else if (returnValue == null) {
        // Either returned null or was 'void' return.
        if (this.responseArgumentUsed || webRequest.isNotModified()) {
            return null;
        }
        else {
            // Assuming view name translation...
            return new ModelAndView().addAllObjects(implicitModel);
        }
    }
    else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
        // Assume a single model attribute...
        addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
        return new ModelAndView().addAllObjects(implicitModel);
    }
    else {
        throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
    }
}

這個方法很重要,它描述的是將 handler 方法返回值解析為 ModelAndView 的過程,從中可以看出返回值可以為很多類型。建議大家都看看。這篇文章不對具體的每個返回值類型進行說明。

3.從官方描述中,也可以看出 ModelAndView 是分為兩部分的,Model 和 View。這裡就分兩部分來說明。其中 View 部分其實是視圖渲染問題。

4.Model 模型。

這裡所說的 Model ,不單單指的就是 Model 具體這個類。而是描述的 SpringMVC 如何將 數據存放到 Model 中,以便在目標頁面中使用。

(1)怎麼存放

上面部分已經說過,通過 ModelAndView 對象來處理返回值問題。那麼就可以通過如下的方式向 Model 中存入數據。如:

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
  ModelAndView mv = new ModelAndView();
  mv.setViewName("success");
  mv.addObject("testKey", "testValue");
  return mv;
}

通過 ModelAndView 對象的 addObject() 方法 可以向模型中添加數據。

事實上,可以在方法的入參處添加 Model 類型 或 Map 類型的參數,可以通過向其中添加數據來完成向模型中數據的添加。如:

@RequestMapping("/testModelAndView02")
public String testModelAndView2(Map map) {
  map.put("testKey", "testValue");
  return "success";
}

為什麼向 handler 方法的入參處添加 Model 類型或 Map 類型參數添加數據,就能完成 模型數據的添加。

通過斷點發現傳入目標方法的 Map 實際類型是 BindingAwareModelMap 這個類型。

源碼解析:

org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#invokeHandlerMethod() 方法中:

ExtendedModelMap implicitModel = new BindingAwareModelMap(); // 在這裡創建的
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);

org.springframework.web.bind.annotation.support.HandlerMethodInvoker#resolveHandlerArguments() 方法中

if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
    if (!paramType.isAssignableFrom(implicitModel.getClass())) {
        throw new IllegalStateException("Argument [" + paramType.getSimpleName() + "] is of type " +
                "Model or Map but is not assignable from the actual model. You may need to switch " +
                "newer MVC infrastructure classes to use this argument.");
    }
    args[i] = implicitModel;//然後在這個地方進行的賦值。
}

可以看出在目標方法處的 所有 Map 類型(包括其子類型)或是 Model 類型(包括其子類型)的參數都會被轉換為 BindingAwareModelMap 這個類型。

這裡對 BindingAwareModelMap 類型進行說明:

(2)存放什麼

從上面可以看出,存放的是鍵值對類型的 Map 或 Model

(3)存放到何處

 看下麵這個例子:

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
  ModelAndView mv = new ModelAndView();
  mv.setViewName("success");
  mv.addObject("testKey", "testValue");
  return mv;
}

success.jsp

通過測試,發現 Model 中的數據預設被存放到了 Request 域中

5.總結

SpringMVC 通過 ModelAndView 解決了 handler 方法返回值問題,明白了 handler 方法返回值可以是何種類型,為什麼說 ModelAndView 解決了 handler方法返回值問題,因為 handler 方法的

返回值最終都會被轉換成 ModelAndView 對象。也詳細的介紹了 Model 可以作為 handler 方法的入參使用,這裡所說的 Model 也不僅僅是指 Model 這個類型,也指實現了 Model 或 Map 介面的

類型。也明白了 Model 中存放的什麼,存放到了哪裡。至此,SpringMVC 方法的返回值問題已經學習完。接下來要學習的是:既然返回值都已經有了,那麼該如何去處理呢?——即handler 方法返回

值處理問題,也指視圖渲染問題。另外,還有兩個註解沒有進行說明,@SessionAttribute和@ModelAttribute,仔細來說,其實他們兩個註解也可以算到 Model 中,這裡會再寫一篇文章來單獨說明它兩。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 相信每一個開發的框架都會有一個工具類,工具類的作用有很多,通常我會將最常用的方法放在工具類里 當然每個開發框架的工具類都會不同,這裡我只收錄了這些方法,希望有更多需求的小伙伴也可以收錄進工具類里 1.取得用戶IP 做Web開發的一定會遇到獲取用戶IP的需求的,無論是用來記錄登錄時間,還是日誌的記錄, ...
  • ...
  • 參考: http://www.pythondoc.com/flask-restful/first.html 什麼是Rest Client-Server:伺服器端與客戶端分離。 Stateless(無狀態):每次客戶端請求必需包含完整的信息,換句話說,每一次請求都是獨立的。 Cacheable(可緩存 ...
  • 直接上代碼: 1. 前端調試代碼: 前端這裡 2. 後端介面: /getConfigInfo.json 介面是配置 jsapi 許可權認證。 /getPhoto.json 是從 微信伺服器下載照片,保存到我們自己的伺服器上。然後將我們自己伺服器的地方返回給前端使用。 ...
  • 1.課前雞湯 2.函數 ...
  • 錯誤情況概述: 啟動應用之後,微信調用 相機拍照 等介面是可以正常使用的, 但是過了一段時間(2個小時左右--token/jsapi_ticket的過期時間),微信調用相機拍照的功能失效,啟用debug模式: //步驟三:通過config介面註入許可權驗證配置 wx.config({ debug: t ...
  • 棧是Vector的一個子類,它實現了一個標準的後進先出的棧。 堆棧只定義了預設構造函數,用來創建一個空棧。 堆棧除了包括由Vector定義的所有方法,也定義了自己的一些方法。 棧常用的五個方法: boolean empty() :測試堆棧是否為空。 object peek():查看棧頂元素,但是不移 ...
  • 枚舉(Enumeration)介面雖然它本身不屬於數據結構,但它在其他數據結構的範疇里應用很廣。 枚舉(The Enumeration)介面定義了一種從數據結構中取回連續元素的方式。 例如,枚舉定義了一個叫nextElement 的方法,該方法用來得到一個包含多元素的數據結構的下一個元素。 枚舉定義 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...