@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" "http://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>
訪問路徑:http://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";
}
// ...
}