【SpringMVC】5.處理模型資料
注意!!!
此文章需要配置了SpringDispatcherServlet
和InternalResourceViewResolver
才能夠使用,如果不會配置,請翻看我【SpringMVC】系列的第一篇文章《【SpringMVC】1. SpringMVC的第一個程式——HelloWorld》
##一、在SpringMVC中如何輸出資料模型?
Spring MVC 提供了以下幾種途徑輸出模型資料:
ModelAndView
: 處理方法返回值型別為 ModelAndView時, 方法體即可通過該物件新增模型資料。Map
及Model
: 入參為org.springframework.ui.Model
org.springframework.ui.ModelMap
或java.uti.Map
時,處理方法返回時,Map
中的資料會自動新增到模型中。@SessionAttributes
: 將模型中的某個屬性暫存到 HttpSession 中,以便多個請求之間可以共享這個屬性。@ModelAttribute
: 方法入參標註該註解後, 入參的物件 就會放到資料模型中。 ##二、輸出方法詳解
###注意!!! 在敲程式碼之前請建立好涉及到的類,在下文以免重複建立:
- 在
com.springmvc.handlers
建立Handler類SpringMVCTest
package com.springmvc.handlers; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping("/springmvc") @Controller public class SpringMVCTest { }
- 在WebRoot根目錄下建立
index.jsp
- 在WEB-INF資料夾下建立views,並在views下建立
success.jsp
檔案 ###(1)ModelAndView
控制器處理方法的返回值如果為ModelAndView
, 則其既包含檢視資訊,也包含模型資料資訊。
格式:
- 新增模型資料:
MoelAndView addObject(String attributeName, Object attributeValue)
ModelAndView addAllObject(Map<String, ?> modelMap)
2. 設定檢視:
void setView(View view )
void setViewName(String viewName)
####相關程式碼及效果
#####1.SpringMVCTest
相關程式碼
/*
* 目標方法的返回值可以是ModelAndView型別 其中包含檢視和模型資訊
* SpringMVC會把ModelAndView的model中資料放入request域物件中
*
*/
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
String viewName = SUCCESS;
ModelAndView modelAndView = new ModelAndView(viewName);
// 新增模型資料到ModelAndView中
modelAndView.addObject("time", new Date());
return modelAndView;
}
#####2.index.jsp
相關程式碼
<a href="springmvc/testModelAndView">testModelAndView</a>
#####3.Success.jsp
相關程式碼
<h1>Success page</h1>
time:${requestScope.time }
#####4.執行TomCat,訪問index.jsp
點選超連結testModelAndView
能夠獲得當前時間,測試ModelAndView
成功
###(2)Map
及Model
####具體步驟:
- Spring MVC 在呼叫方法前會建立一個隱含的模型物件作為模型資料的儲存容器。
- 如果方法的入參為
Map
或Model
型別,SpringMVC會將隱含模型的引用傳遞給這些入參。在方法體內,開發者可以通過這個入參物件訪問到模型中的所有資料,也可以向模型中新增新的屬性資料。
/*
* 目標方法可以新增Map型別,實際上也可以時 Model 型別或者 ModelMap 型別的引數
*/
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map) {
map.put("names", Arrays.asList("Tom", "Jerry", "Mike"));
return SUCCESS;
}
#####2.index.jsp
相關程式碼
<a href="springmvc/testMap">testMap</a>
#####3.Success.jsp
相關程式碼
<h1>Success page</h1>
<br> names:${requestScope.names }
#####4.執行TomCat,訪問index.jsp
點選超連結testMap
能夠獲得指定的姓名,即為成功
@SessionAttributes
- 若希望在多個請求之間共用某個模型屬性資料,則可以在控制器類上標註一個
@SessionAttributes
, Spring MVC將在模型中對應的屬性暫存到HttpSession 中。 - @SessionAttributes除了可以通過屬性名指定需要放到會話中的屬性外,還可以通過模型屬性的物件型別指定哪些模型屬性需要放到會話中
@SessionAttributes(types=User.class)
會將隱含模型中所有型別為 User.class 的屬性新增到會話中。@SessionAttributes(value={“user1”, “user2”})
@SessionAttributes(types={User.class, Dept.class})
@SessionAttributes(value={“user1”, “user2”}, types={Dept.class})
####相關程式碼及效果
#####1.SpringMVCTest
相關程式碼
注意在類名頭上
/**
* 注意控制類前面加@SessionAttributes
*/
@SessionAttributes(value = { "user" }, types = { String.class })
@RequestMapping("/springmvc")
@Controller
public class SpringMVCTest {
/*
* @SessionAttributes 使用時只能在類上面使用@SessionAttributes註解
* 除了可以通過屬性名指定需要放到會話中的屬性外(實際上是使用value 屬性值)
* 還可以通過模型屬性的物件型別指定哪些模型屬性需要放到會話中(實際上是使用types 屬性值)
*/
@RequestMapping("/testSessionAttributes")
public String testSessionAttributes(Map<String, Object> map) {
User user = new User("tom", "123456", "[email protected]", 15);
map.put("user", user);// 使用value屬性值
map.put("school", "FSPT");// 使用types屬性值
return SUCCESS;
}
}
#####2.index.jsp
相關程式碼
<a href="springmvc/testSessionAttributes">testSessionAttributes</a>
#####3.Success.jsp
相關程式碼
<h1>Success page</h1>
<br> request user:${requestScope.user }
<br>-----------------------------------------
<br> session user:${sessionScope.user }
#####4.執行TomCat,訪問index.jsp
點選超連結testSessionAttributes
能夠在Session域中獲得user屬性即為成功。
###(4)@ModelAttribute
-
在方法定義上使用
@ModelAttribute
註解: Spring MVC在呼叫目標處理方法前,會先逐個呼叫在方法級上標註了@ModelAttribute
的方法。 -
在方法的形參前使用
@ModelAttribute
註解:
- 可以從隱含物件中獲取隱含的模型資料中獲取物件,再將請求引數繫結到物件中,再傳入形參
- 將方法形參物件新增到模型中
- 使用場景:
舉個例子,使用者在填寫了一個表單,表單中包含資料項
id
,name
,sex
,把它們抽象出來封裝到到User
中。隔了一段時間,使用者發現寫錯名字了,需要修改name,如果直接new一個User
物件出來,由於只改動了name而已,其他的屬性由於沒有獲得到資料從而導致id
和sex
屬性都為null
,這樣顯然不太合適。而@ModelAttribute
這是為了解決這種狀況而出現的。
package com.springmvc.entities;
public class User {
private Integer id;
private String username;
private String password;
private String email;
private int age;
private Address address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + ", email=" + email + ", age="
+ age + "]";
}
public User(Integer id, String username, String password, String email, int age) {
super();
this.id = id;
this.username = username;
this.password = password;
this.email = email;
this.age = age;
}
public User(String username, String password, String email, int age) {
super();
this.username = username;
this.password = password;
this.email = email;
this.age = age;
}
public User() {
}
}
#####2.SpringMVC
相關程式碼`
/*
* 1.有ModelAttribute標記的方法,會在每個目標方法執行之前被SpringMVC呼叫 [email protected]
* 註解也可以用來修飾目標方法的POJO型別的入參,其value屬性值有如下作用
* 1)SpringMVC會使用value屬性值在Implication中查詢對應的物件,若存在則直接傳入目標方法的入參中
* 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", "123456", "[email protected]", 12);
System.out.println("從資料庫獲取一個物件" + 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 作為引數傳遞給目標方法的入參.
*
* 注意:在@ModelAttribute 修飾的方法中,放入到Map 時的鍵需要和目標方法入參型別的第一個字母小寫字串一致,如:
* 在方法getUser中map.put("user", user); testModelAttribute(User user)
* 入參的第一個單詞(即型別),需要和鍵名一致,只是入參的第一個字母需要大寫,鍵名全為小寫
*/
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user) {
System.out.println("修改:" + user);
return SUCCESS;
}
#####3.index.jsp
相關程式碼
<!--
模擬修改操作
1.原始資料:id:1、username:Tom、passowrd:123456、email:[email protected],age:12
2.密碼不能被修改
3.表單回顯,模擬操作直接寫在表單對應的屬性值
-->
<form action="springmvc/testModelAttribute">
<input type="hidden" name="id" value="1"/>
username:<input type="text" name="username" value="Tom">
<br>
email:<input type="email" name="email" value="[email protected]">
<br>
age:<input type="text" name="age" value="12">
<br>
<input type="submit">
</form>
#####4.Success.jsp
相關程式碼
<br> request user:${requestScope.user }
<br>-----------------------------------------
#####執行TomCat,訪問index.jsp
原本資料是
現在修改age為18
得到結果:
@SessionAttributes(value = { "user" }, types = { String.class })
沒有刪除,所以在Session域裡面也有User
,預設只會存在request
域中