註解@ModelAttribute的詳細總結

引導

在項目中,controller中經常看到@ModelAttribute或者@ModelAttribute(“someName”)這種用法,這到底是個什麼意思呢?本文帶你一探究竟。

作用

@ModelAttribute,即org.springframework.web.bind.annotation.ModelAttribute,是spring的一個註解,可以用在方法或者屬性上。被該註解標註的方法會在該controller的請求方法執行之前執行,比如你定義一個TestController,其中一個方法test()標註@RequestMapping("/api/test"),然後發起一個請求/api/test,那麼在test執行前,該類中所有@ModelAttribute標註的方法會先執行,執行之後的結果會放在Model中,後續test方法可以通過Model獲得一些全局屬性。那麼問題來了,Model是什麼,這個暫時可以理解爲該類的一個全局屬性,是一個map形式。
簡單理解就是

  • 該註解標註的方法會在handler具體方法執行之前,作爲全局屬性存放在Model,供後續handler具體方法執行的時候調用

在知道了ModelAttribute的執行時間和作用,我們來看看這個註解怎麼用。

使用方法

該註解標註的方法可以帶有和請求方法一樣的參數,比如請求方法帶有一個String userId,那麼該方法也可以帶有,兩個方法對應的值是一樣的,但要注意的是:

  • 能自動獲取的值是可以用@RequestParam拿到的值,即get請求的?後接的參數或者post請求的body裏的參數,且如果是body裏的參數,則此post請求對應的Content-Type爲application/x-www-form-urlencoded,如果爲application/json則無法用@RequestParam獲取到,@ModelAttribute方法的參數也就獲取不到,參數爲null。
private static final String USER_ID = "userId";
@ModelAttribute(USER_ID)
    public String getUserId(String userId) {
        System.out.println(userId);
        return "my" + userId;
    }

如上面的代碼,就是自動獲取的方式,我的測試請求可以用如下格式:

Get /api/test/model?userId=123
Post /api/test
Content-Type: application/x-www-form-urlencoded

userId=234


另一種方式是在方法的參數前標明獲取方式:

private static final String USER_ID = "userId";
@ModelAttribute(USER_ID)
    public String getUserId(@PathVariable("userId") String userId) {
        System.out.println(userId);
        return "my" + userId;
    }

如上,這種情況下對應的請求需要在路徑中包含userId,且需要注意的是:

如果用了這種方式,那麼這個類中所有的請求方法對應的uri都路徑中都需要包含userId的佔位符
比如我請求瞭如下接口

Get /api/test/model/tt
//會報錯"Missing URI template variable 'userId' for method parameter of type String"

同理,還可以用@RequestParam @RequestBody等獲取參數

使用的話我們可以直接用如下的方式

public Object getSomeThing1(@ModelAttribute(USER_ID) String userID) {
        
        return userID;
}

不加參數怎麼用呢?
舉個例子:

@ModelAttribute
    public UserData getuserData() {
        UserData userData = new UserData();
        userData.setUserId("id");
        Map<String, String> map = new HashMap<>();
        map.put("testKey", "testValue");
        userData.setData(map);
        System.out.println(userData);
        return userData;
    }

我們定義了一個getuserData方法,返回了一個userData實例,由於我們沒有像上面定義getUserId方法一樣直接指定@ModelAttribute的value值,返回後對應的key默認爲返回類型且首字母小寫,即userData。通過定義這個方法,我們可以在model中獲得一個key爲userData的value,值爲該方法的返回值。

如下爲方法執行後model中的值。

{
    "userData": {
        "userId": "id",
        "data": {
            "testKey": "testValue"
        }
    }
}

那麼定義好了,怎麼在方法中用呢?

@RequestMapping(value = "/api/test/model/post", method = RequestMethod.POST)
    public Object postUserData(@ModelAttribute UserData data) {
        return data;
    }

這樣我們就可以獲得一個UserData實例了,及時我的請求中沒有任何參數。
注意:上面的例子我們獲取了一個userData實例,如果對不同的請求我想得到不同的實例怎麼辦呢?

  • 首先可以通過類似於上面getUserId(String UserId)這種方法實現,也就是你的請求參數中添加一個userId,在@ModelAttribute修飾的方法中構造自己的UserData實例,這樣就可以

  • 其次我們可以用下面的getuserData()的方式,只傳遞一個userId參數,然後代碼不變,由於ModelAttribute會自動獲取@RequestParam可以獲取到的值,會用請求中的值替換model中預設的值。

例子:

Post /api/test/model/post?userId=popo

最後返回的值有原來的

{
  "userId": "id",
  "data": {
    "testKey": "testValue"
  }
}

變成了

{
  "userId": "popo",
  "data": {
    "testKey": "testValue"
  }
}

這個變化只是因爲我的請求加了參數,代碼未變。


總結幾點

  • @ModelAttribute適合給一個controller增加一些全局的變量

  • 如果返回一個String值,可以用上面例子中的@ModelAttribute(USER_ID) String userID 這種方式,也可以在請求方法中直接引入model或者modelMap進行get,但不可以直接@ModelAttribute String userID。

  • 如果返回的是一個對象,一個實例,那麼可以直接@ModelAttribute UserData data使用,也可以@ModelAttribute(“userData”) UserData data使用,還可以用Model或者ModelMap進行get.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章