在一個Controller內,被@ModelAttribute標注的方法會在此controller的每個handler方法執行前被執行。
被@ModelAttribute標注的方法的參數綁定規則和普通handler方法相同。
可以理解為:
- 請求到達Controller后,不論其他handler方法的RequestMapping值是多少,請求都會路由至被@ModelAttribute標注的方法;
- 由springMVC再對request執行一次forward,路由至真正的handler方法。
一 @ModelAttribute用於注解方法
1 方法返回類型為void
這種情況,@ModelAttribute只是單純的作為請求路由的第一站,使用者可在方法內部操作Model和Request等參數實現功能。
對於如下請求:
http://localhost:8080/TestModelAttributeController/testHandler.action?reqParam=123
對應的Controller:
@Controller @RequestMapping("/TestModelAttributeController") public class TestModelAttributeController { @ModelAttribute public void modelAttributeMethod(HttpServletRequest request, String reqParam, Model model){ model.addAttribute("reqParam",reqParam); request.setAttribute("methodParam","Hello ModelAttribute"); } @RequestMapping("/testHandler") public String testHandler(){ return "testModelAttribute"; } }
testModelAttribute.jsp如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>${reqParam}</h1> <h1>${methodParam}</h1> </body> </html>
最終可以在頁面中看到:
123
Hello ModelAttribute
2 方法返回類型不為void
這種情況,@ModelAttribute會將返回值放到Model中,並將該類型名稱的首字母小寫作為model中的屬性名。
請求地址和參數不變。
對應的Controller:
@ModelAttribute public User userModelAttributeMethod2(){ User user = new User(); user.setAge(31); user.setName("James"); user.setEmail("123456@qq.com"); return user; //相當於model.addAttribute("user",user); } @RequestMapping("/testHandler") public String testHandler(Model model){ System.out.println(model.containsAttribute("user")); //true return "testModelAttribute";
}
對應的jsp頁面
<h1>${user.age}</h1> <h1>${user.email}</h1> <h1>${user.name}</h1>
實際上,對於返回類型為void的方法,@ModelAttribute也會在model中添加一對鍵值對,“void”->"null"
3 方法返回類型不為void,且@ModelAttribute指定屬性名稱
這種情況下,@ModelAttribute會將返回值放到Medel中,且對應的key值為@ModelAttribute置頂的屬性名
對應的Controller:
@ModelAttribute("myUser") public User userModelAttributeMethod2(){ User user = new User(); user.setAge(31); user.setName("James"); user.setEmail("123456@qq.com"); return user; //相當於model.addAttribute("user",user); } @RequestMapping("/testHandler") public String testHandler(Model model){ System.out.println(model.containsAttribute("user")); //true return "testModelAttribute"; }
對應的jsp頁面:
<h1>${myUser.age}</h1> <h1>${myUser.email}</h1> <h1>${myUser.name}</h1>
4 @ModelAttribute和@RequestMapping注解同一個方法
這種情況下:
- 在controller處理其他請求時,不會再首先進入被@ModelAttribute和@RequestMapping同時注解的方法;
- 該方法的返回值不再是視圖的邏輯名稱,而是按照@ModelAttribute的規則被加入到Model中;
- @RequestMapping注解的value值具有兩個作用
- 作為URI,實現請求的路由;
- 作為此次請求的邏輯視圖名(嚴格來說此時視圖的邏輯視圖名是:controller的RequestMapping值+method的RequestMapping值)
@Controller @RequestMapping("/TestModelAttributeController") public class TestModelAttributeController { @RequestMapping("/testModelAttribute") @ModelAttribute("result") public String testModelAttribute(){ return "excellent"; }
<body> <h1>${result}</h1> </body>
如上Controller和jsp:
testModelAttribute方法的作用是:
- 處理路徑為 /TestModelAttributeController/testModelAttribute*的請求
- 將鍵值對"result"->"excellent"放至model中,為視圖渲染提供數據
- 返回邏輯視圖名 /TestModelAttributeController/testModelAttribute
二 @ModelAttribute注解方法入參
@ModelAttribute("attrName")用在方法入參上時,作用為:
- 從當前的隱式model對象中取key值attrName所對應的attrValue值,並將attrValue賦給被注解的參數。
- 而且自動暴露為模型數據用於視圖頁面展示時使用
1 @ModelAttribute指定注解的value值attrName
如下所示,myUser和newParam兩個model屬性對應的attrValue值,將被賦值給方法入參。
@ModelAttribute("myUser") public User userModelAttributeMethod2(Model model){ User user = new User(); user.setAge(31); user.setName("James"); user.setEmail("123456@qq.com"); model.addAttribute("newParam","new parameter"); return user; } @RequestMapping("/testHandler") public String testHandler(@ModelAttribute("myUser") User user,@ModelAttribute("newParam") String newParam){ System.out.println(user); System.out.println(newParam); return "testModelAttribute";
2 @ModelAttribute注解value值缺省
這時默認的attrName為類型名稱的首字母小寫。
如下例,user能夠從model中獲取,但是 newParam從model中獲取的值為null
@ModelAttribute("user") //此處必須是 user public User userModelAttributeMethod2(Model model){ User user = new User(); user.setAge(31); user.setName("James"); user.setEmail("123456@qq.com"); model.addAttribute("newParam","new parameter"); return user; } @RequestMapping("/testHandler") public String testHandler(@ModelAttribute User user,@ModelAttribute String newParam){ System.out.println(user); System.out.println(newParam); return "testModelAttribute"; }
3 入參不使用@ModelAttribute注解
這種情況下,簡單類型參數不會從model中取值,簡單類型定義由 org.springframework.beans.BeanUtils#isSimpleValueType指定,如下:

/** * Check if the given type represents a "simple" value type: * a primitive, an enum, a String or other CharSequence, a Number, a Date, * a URI, a URL, a Locale or a Class. * @param clazz the type to check * @return whether the given type represents a "simple" value type */ public static boolean isSimpleValueType(Class<?> clazz) { return (ClassUtils.isPrimitiveOrWrapper(clazz) || Enum.class.isAssignableFrom(clazz) || CharSequence.class.isAssignableFrom(clazz) || Number.class.isAssignableFrom(clazz) || Date.class.isAssignableFrom(clazz) || URI.class == clazz || URL.class == clazz || Locale.class == clazz || Class.class == clazz); }
非簡單類型會從model中取值,這時默認的attrName為類型名稱的首字母小寫。
如下例,user能夠從model中獲取,但是newParam是簡單類型,所以不會從model中取值。
4 使用request參數和@ModelAttribute同時為同一個入參賦值
這種情況需要根據入參的類型區別對待
- 對於非簡單類型,spring會先使用@ModelAttribute為參數賦值,然后使用request的參數對入參的屬性值進行覆蓋;
- 對於簡單類型,spring會使用@ModelAttribute為參數賦值,忽略request參數;
如下例所示,URL為:
這時user的name和age屬性最終會被設置為request對應的參數值(也就是實現了對象合並),而reqParam的值最終會采用@ModelAttribute得到的值。
@ModelAttribute("user") //此處必須是 user public User userModelAttributeMethod2(Model model){ User user = new User(); user.setAge(31); user.setName("James"); user.setEmail("123456@qq.com"); model.addAttribute("reqParam","from @ModelAttribute"); return user; } @RequestMapping("/testHandler") public String testHandler( @ModelAttribute("user") User user, @ModelAttribute("reqParam") String reqParam){ System.out.println(user); //user 對象的屬性會被request的參數覆蓋 System.out.println(reqParam); //reqParam參數的值始終是@ModelAttribute中的值,不會被覆蓋 return "testModelAttribute"; }
三 @ModelAttribute注解方法返回值
此時@ModelAttribute的作用是將返回值添加至model,而這時的邏輯視圖名稱為:controller的RequestMapping值+method的RequestMapping值。
其實,這種用法和 @ModelAttribute和@RequestMapping注解同一個方法相同。