[email protected] 注解:Spring MVC 在調用目标處理方法前,會先逐個調用在方法級上标注了@ModelAttribute 的方法。
(注意是每個目标方法之前都會調用該方法)
2.代碼示例
2.1 需求。
正常更新資料庫中的一條記錄,記錄中有的字段時不能發生改變的,比如記錄的建立時間。這樣的話,會從jsp頁面
得到隻有以上為例,兩個字段的參數,放入一個建立的對象,此時該對象隻有兩個屬性,有一個不能修改的為空。這樣
在執行更新操作的時候,就會覆寫資料的記錄,使得不能修改的字段的值為空。為了解決該問題,有了如下方法:
在得到jsp頁面的兩個參數的時候,先從資料庫把該條記錄查出來,這樣不能修改的屬性也有了資料。再把jsp頁面的
參數指派給對應的屬性,執行更新操作。
2.2 jsp頁面請求。
<form action="springMVC/testModelAttribute" method="Post">
<input type="hidden" name="id" value="1"/>
username: <input type="text" name="username" value="Tom"/>
<br>
email: <input type="text" name="email" value="[email protected]"/>
<br>
age: <input type="text" name="age" value="10"/>
<br>
<input type="submit" value="Submit"/>
</form>
2.3 目标方法。通過@ModelAttribute注解的方法,在調用目标方法之前,先調用改方法,裝配model,查出資料。指派
給model。
/**
*[email protected] 标記的方法,會在每個目标方法執行之前被SpringMVC調用
*[email protected]注解也可以用來修飾目标方法POJO類型的入參,其value屬性
* 值有如下的作用:
* 2.1 SpringMVC會使用value的屬性值,在implicitModel中查找對應的對象,
* 若存在則會直接傳入到目标方法的入參中。
* 2.2 SpringMVC會以value為key,POJO類型的隊對象為value,存入到request中。
*/
@ModelAttribute
public void getUser(@RequestParam(value="id",required=false) Integer id,
Map<String,Object> map){
if(id != null){
User user = new User(1, "Tom", "1234586", "[email protected]", 12);
System.out.println("database from :" + user);
map.put("user", user);
}
}
/**
* 運作流程:
* 1. 執行 @ModelAttribute 注解修飾的方法: 從資料庫中取出對象, 把對象放入到了 Map 中. 鍵為: user
* 2. SpringMVC 從 Map 中取出 User 對象, 并把表單的請求參數賦給該 User 對象的對應屬性.
* 3. SpringMVC 把上述對象傳入目标方法的參數.
*
* 注意: 在 @ModelAttribute 修飾的方法中, 放入到 Map 時的鍵需要和目标方法入參類型的第一個字母小寫的字元串一緻!
*
* SpringMVC 确定目标方法 POJO 類型入參的過程
* 1. 确定一個 key:
* 1). 若目标方法的 POJO 類型的參數木有使用 @ModelAttribute 作為修飾, 則 key 為 POJO 類名第一個字母的小寫
* 2). 若使用了 @ModelAttribute 來修飾, 則 key 為 @ModelAttribute 注解的 value 屬性值.
* 2. 在 implicitModel 中查找 key 對應的對象, 若存在, 則作為入參傳入
* 1). 若在 @ModelAttribute 标記的方法中在 Map 中儲存過, 且 key 和 1 确定的 key 一緻, 則會擷取到.
* 3. 若 implicitModel 中不存在 key 對應的對象, 則檢查目前的 Handler 是否使用 @SessionAttributes 注解修飾,
* 若使用了該注解, 且 @SessionAttributes 注解的 value 屬性值中包含了 key, 則會從 HttpSession 中來擷取 key 所
* 對應的 value 值, 若存在則直接傳入到目标方法的入參中. 若不存在則将抛出異常.
* 4. 若 Handler 沒有辨別 @SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 key, 則
* 會通過反射來建立 POJO 類型的參數, 傳入為目标方法的參數
* 5. SpringMVC 會把 key 和 POJO 類型的對象儲存到 implicitModel 中, 進而會儲存到 request 中.
*
* 源代碼分析的流程
* 1. 調用 @ModelAttribute 注解修飾的方法. 實際上把 @ModelAttribute 方法中 Map 中的資料放在了 implicitModel 中.
* 2. 解析請求處理器的目标參數, 實際上該目标參數來自于 WebDataBinder 對象的 target 屬性
* 1). 建立 WebDataBinder 對象:
* ①. 确定 objectName 屬性: 若傳入的 attrName 屬性值為 "", 則 objectName 為類名第一個字母小寫.
* *注意: attrName. 若目标方法的 POJO 屬性使用了 @ModelAttribute 來修飾, 則 attrName 值即為 @ModelAttribute
* 的 value 屬性值
*
* ②. 确定 target 屬性:
* > 在 implicitModel 中查找 attrName 對應的屬性值. 若存在, ok
* > *若不存在: 則驗證目前 Handler 是否使用了 @SessionAttributes 進行修飾, 若使用了, 則嘗試從 Session 中
* 擷取 attrName 所對應的屬性值. 若 session 中沒有對應的屬性值, 則抛出了異常.
* > 若 Handler 沒有使用 @SessionAttributes 進行修飾, 或 @SessionAttributes 中沒有使用 value 值指定的 key
* 和 attrName 相比對, 則通過反射建立了 POJO 對象
*
* 2). SpringMVC 把表單的請求參數賦給了 WebDataBinder 的 target 對應的屬性.
* 3). *SpringMVC 會把 WebDataBinder 的 attrName 和 target 給到 implicitModel.
* 近而傳到 request 域對象中.
* 4). 把 WebDataBinder 的 target 作為參數傳遞給目标方法的入參.
*/
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user){
System.out.println("修改:"+user);
return SUCCESS;
}
2.4 解讀:我在有@ModelAttribute注解的方法中查到了一天記錄,并且把它放入到了map集合中 map.put("user", user);
然後我在目标方法(testModelAttribute(User user))中就直接擷取到了上面的放入map中的記錄(user),在
2.3裡面介紹了SpringMVC是如何擷取該記錄的。在我沒有給目标方法的POJO入參添加@ModelAttribute注解之前
我之是以能夠在目标方法中準确地得到的放入map中的user,是因為我在 map.put("user", user)時,map的key值
是下面目标方法POJO入參的類型的第一個字母的小寫,是以我可以得到。
那麼,一旦我在 map.put("user", user)時,key放入的值不是user,而是,abc等其他,不是目标方法POJO入參的類
型的第一個字母的小寫的字元串時,在沒有給目标方法的POJO入參添加@ModelAttribute注解之前,因為SpringMVC
找不到對應的資料,(因為我放入map的時候沒有按照規則命名),是以會根據目标方法的POJO類入參的類型依靠
反射去擷取一個新的模型,那麼,這個新的model是沒有從資料庫查出來的某些屬性的。
但是,如果在目标方法的POJO入參添加@ModelAttribute注解,就可以不用按照規則在map中放入model,但是此時
@ModelAttribute(“aaa”)注解的value值,必須和放入map中的model的key值一緻。(map.put("aaa", user))
這就是@ModelAttribute使用在目标方法POJO類型入參的作用。
在正常情況下,是不用在目标方法POJO類型入參出添加@ModelAttribute注解的,預設我們都會在map放入的時候
使用POJO類型入參的第一個字母小寫的字元串。