1. 程式人生 > >【SpringMVC】5.處理模型資料

【SpringMVC】5.處理模型資料

注意!!! 此文章需要配置了SpringDispatcherServletInternalResourceViewResolver才能夠使用,如果不會配置,請翻看我【SpringMVC】系列的第一篇文章《【SpringMVC】1. SpringMVC的第一個程式——HelloWorld》 ##一、在SpringMVC中如何輸出資料模型?

Spring MVC 提供了以下幾種途徑輸出模型資料:

  • ModelAndView: 處理方法返回值型別為 ModelAndView時, 方法體即可通過該物件新增模型資料。
  • MapModel: 入參為org.springframework.ui.Model
    org.springframework.ui.ModelMapjava.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, 則其既包含檢視資訊,也包含模型資料資訊。

格式:

  1. 新增模型資料: 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)MapModel

####具體步驟:

  1. Spring MVC 在呼叫方法前會建立一個隱含的模型物件作為模型資料的儲存容器。
  2. 如果方法的入參為 MapModel型別,SpringMVC會將隱含模型的引用傳遞給這些入參。在方法體內,開發者可以通過這個入參物件訪問到模型中的所有資料,也可以向模型中新增新的屬性資料。
![這裡寫圖片描述](https://img-blog.csdn.net/20180823220831665?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzNTk2OTc4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
####相關程式碼及效果 #####1.`SpringMVCTest`相關程式碼
	/*
	 * 目標方法可以新增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 能夠獲得指定的姓名,即為成功 這裡寫圖片描述

由於Success.jsp之前的程式碼沒刪除,同學顯示的頁面和我的不一樣,但是隻要姓名能夠顯示出來即可
###(3)@SessionAttributes
  • 若希望在多個請求之間共用某個模型屬性資料,則可以在控制器類上標註一個 @SessionAttributes, Spring MVC將在模型中對應的屬性暫存到HttpSession 中。
  • @SessionAttributes除了可以通過屬性名指定需要放到會話中的屬性外,還可以通過模型屬性的物件型別指定哪些模型屬性需要放到會話中
  1. @SessionAttributes(types=User.class)會將隱含模型中所有型別為 User.class 的屬性新增到會話中。
  2. @SessionAttributes(value={“user1”, “user2”})
  3. @SessionAttributes(types={User.class, Dept.class})
  4. @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

  1. 在方法定義上使用 @ModelAttribute註解: Spring MVC在呼叫目標處理方法前,會先逐個呼叫在方法級上標註了@ModelAttribute 的方法。

  2. 在方法的形參前使用@ModelAttribute 註解:

  1. 可以從隱含物件中獲取隱含的模型資料中獲取物件,再將請求引數繫結到物件中,再傳入形參
  2. 將方法形參物件新增到模型中
  1. 使用場景: 舉個例子,使用者在填寫了一個表單,表單中包含資料項id,name,sex,把它們抽象出來封裝到到User中。隔了一段時間,使用者發現寫錯名字了,需要修改name,如果直接new一個User物件出來,由於只改動了name而已,其他的屬性由於沒有獲得到資料從而導致idsex屬性都為null,這樣顯然不太合適。而@ModelAttribute這是為了解決這種狀況而出現的。
![這裡寫圖片描述](https://img-blog.csdn.net/20180824105504947?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzNTk2OTc4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
未使用`@ModelAttribute`前
![這裡寫圖片描述](https://img-blog.csdn.net/20180824105704335?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzNTk2OTc4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
使用`@ModelAttribute`後
####相關程式碼及效果 #####1.在`com.springmvc.entities`建立實體類`User`
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

這裡寫圖片描述

得到結果: 這裡寫圖片描述

由於我在SpringMVCTest在類名頭部修飾的@SessionAttributes(value = { "user" }, types = { String.class })沒有刪除,所以在Session域裡面也有User,預設只會存在request域中
這裡寫圖片描述
控制檯輸出結果