@ModelAttribute 的使用
- 2020 年 4 月 20 日
- 筆記
@ModelAttribute
註解可被應用在 方法 或 方法參數 上。
對方法使用 @ModelAttribute 註解:
註解在方法上的@ModelAttribute
說明了方法的作用是用於添加一個或多個屬性到model上。這樣的方法能接受與@RequestMapping
註解相同的參數類型,只不過不能直接被映射到具體的請求上。
@ModelAttribute 方法會先被調用
在同一個控制器中,註解了@ModelAttribute
的方法實際上會在@RequestMapping
方法之前被調用。以下是幾個例子:
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ModelAttributeController {
@ModelAttribute
public void init(Model model) {
System.out.println("@RequestMapping方法");
}
@RequestMapping("/model-attribute")
public String get() {
System.out.println("@ModelAttribute方法");
return "model-attribute";
}
}
控制台輸出的結果如下:
無返回值的 @ModelAttribute 方法
一般被 @ModelAttribute 註解的無返回值控制器方法被用來向 Model 對象中設置參數,在控制器跳轉的頁面中可以取得 Model 中設置的參數:
ModelAttributeController.java:
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ModelAttributeController {
@ModelAttribute
public void init(Model model) {
model.addAttribute("arg", "model中設置的參數");
}
@RequestMapping("/model-attribute")
public String get() {
return "model-attribute";
}
}
model-attribute.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "//www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<input type="text" value=${ arg } />
</body>
</html>
訪問路徑://localhost:8080/model-attribute,頁面效果如下:
可以發現,我們在被 @RequestMapping 標註的方法中並沒有設置任何參數,但是頁面卻取得了在 @ModelAtribute 標註的方法設置的參數。由此可知,可以使用 @ModelAttribute 標註的方法來設置其他 @ReqeustMapping 方法的公用參數,這樣就不用每一個方法都設置共同的 Model 參數。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ModelAttributeController {
@ModelAttribute
public void init(Model model) {
model.addAttribute("arg", "model中設置的參數");
}
@RequestMapping("/model-attribute")
public String get() {
return "model-attribute";
}
@RequestMapping("/demo")
public String demo() {
return "demo";
}
}
在 demo.jsp 和 model-attribute.jsp 中都可以獲得名為 “arg” 的 Model 參數。
有返回值的 @ModelAttribute 方法
有返回值的 @ModelAttribute 方法和沒有返回值的 @ModelAttribute 方法的區別就是,沒有返回值的 @ModelAttribute 方法使用 model.addAttribute(String key, Object value); 來向 Model 中增加參數。而有返回值的 @ModelAttribute 方法則直接將需要增加的參數返回。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import com.pudding.bean.User;
@Controller
public class ModelAttributeController {
@ModelAttribute
public User init(Model model) {
User user = new User("小明", 18);
return user;
}
@RequestMapping("/model-attribute")
public String get() {
return "model-attribute";
}
}
上面的程式碼的作用是,向 Model 中增加了 key 為 “user” value 為 user 對象的鍵值對。這個 user 對象中有 username = “小明”,age = 18 的屬性。
使用 @ModelAttribute(“key”) 來顯示指定屬性名
屬性名沒有被顯式指定的時候又當如何呢?在這種情況下,框架將根據屬性的類型給予一個默認名稱。舉個例子,若方法返回一個User
類型的對象,則默認的屬性名為”user”。你可以通過設置@ModelAttribute
註解的值來改變默認值。當向Model
中直接添加屬性時,請使用合適的重載方法addAttribute(..)
-即,帶或不帶屬性名的方法。
以下程式碼將會向 Model 中設置一個 key 為 “person” ,value 為 user對象的鍵值對:
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import com.pudding.bean.User;
@Controller
public class ModelAttributeController {
@ModelAttribute("person")
public User init(Model model) {
User user = new User("小明", 18);
return user;
}
@RequestMapping("/model-attribute")
public String get() {
return "model-attribute";
}
}
@ModelAttribute 和 @RequestMapping 註解在同一個方法上
如果 @ModelAttribute 和 @RequestMapping 註解在同一個方法上,那麼代表給這個請求單獨設置 Model 參數。此時返回的值是 Model 的參數值,而不是跳轉的地址。跳轉的地址是根據請求的 url 自動轉換而來的。比如下面的例子中跳轉頁面不是 HelloWorld.jsp 而是 model-attribute.jsp。並且參數只有在 model-attribute.jsp 中能夠取得,而 demo.jsp 中不能取得。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ModelAttributeController {
@ModelAttribute("message")
@RequestMapping("/model-attribute")
public String init(Model model) {
return "HelloWorld";
}
@RequestMapping("/demo")
public String get() {
return "demo";
}
}
在方法參數上使用 @ModelAttribute 註解:
如上一小節所解釋,@ModelAttribute
註解既可以被用在方法上,也可以被用在方法參數上。這一小節將介紹它註解在方法參數上時的用法。
數據綁定
註解在方法參數上的@ModelAttribute
說明了該方法參數的值將由model中取得。如果model中找不到,那麼該參數會先被實例化,然後被添加到model中。在model中存在以後,請求中所有名稱匹配的參數都會填充到該參數中。這在Spring MVC中被稱為數據綁定,一個非常有用的特性,節約了你每次都需要手動從表格數據中轉換這些欄位數據的時間。
和 BindingResult 配合使用
使用 @ModelAttribute
進行數據綁定之後,可以使用 BindingResult
來返回數據驗證結果。數據驗證可以使用 hibernate validation 的 @Valid
標籤或者 spring Validator 校驗機制的 @Validated
配合 BindingResult 使用。 或者自定義校驗器來返回 BindingResult 對象來進行校驗。你可以通過Spring的 <errors>
表單標籤來在同一個表單上顯示錯誤資訊。
@Valid 校驗器:
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
@Validated 校驗器:
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@Validated @ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
自定義校驗器:
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {
// 自己編寫一個校驗方法來處理 result 對象
new PetValidator().validate(pet, result);
if (result.hasErrors()) {
return "petForm";
}
// ...
}