第 13 章 數(shù)據(jù)綁定

通過第 12 章的學(xué)習(xí)歪泳,大家已經(jīng)知道后臺的請求處理方法可以包含多種參數(shù)類型萝勤。 在實際的項目開發(fā)中,多數(shù)情況下客戶端會傳遞帶有不同參數(shù)的請求呐伞,那么后臺是如何綁定并獲取這些請求參數(shù)的呢?那么接下來敌卓,本章將對 Spring MVC 框架中的數(shù)據(jù)綁定進(jìn)行詳細(xì)講解。

數(shù)據(jù)綁定介紹

在執(zhí)行程序時伶氢, Spring MVC 會根據(jù)客戶端請求參數(shù)的不同趟径,將請求消息中的信息以一定的方式轉(zhuǎn)換并綁定到控制器類的方法參數(shù)中 瘪吏。 這種將請求消息數(shù)據(jù)與后臺方法參數(shù)建立連接的過程就是 Spring MVC 中的數(shù)據(jù)綁定。
在數(shù)據(jù)綁定過程中蜗巧, Spring MVC 框架會通過數(shù)據(jù)綁定組件( DataBinder )將請求參數(shù)串的內(nèi)容進(jìn)行類型轉(zhuǎn)換掌眠,然后將轉(zhuǎn)換后的值賦給控制器類中方法的形參,這樣后臺方法就可以正確綁定并獲取客戶端請求攜帶的參數(shù)了幕屹。 整個數(shù)據(jù)綁定的過程如圖所示蓝丙。



關(guān)于上圖中信息處理過程的步驟描述如下。
( 1 ) Spring MVC 將 ServletRequest 對象傳遞給 DataBinder望拖。
( 2 )將處理方法的入?yún)ο髠鬟f給 DataBinder渺尘。
( 3 ) DataBinder 調(diào)用 ConversionService 組件進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換、數(shù)據(jù)格式化等工作说敏,并 將 ServletRequest 對象中的消息填充到參數(shù)對象中 沧烈。
( 4 )調(diào)用 Validator 組件對已經(jīng)綁定了請求消息數(shù)據(jù)的參數(shù)對象進(jìn)行數(shù)據(jù)合法性校驗。
( 5 )校驗完成后會生成數(shù)據(jù)綁定結(jié)果 BindingResult 對象像云, Spring MVC 會將 BindingResult 對象中的內(nèi)容賦給處理方法的相應(yīng)參數(shù)锌雀。
根據(jù)客戶端請求參數(shù)類型和個數(shù)的不同,我們將 Spring MVC 中的數(shù)據(jù)綁定主要分為簡單數(shù)據(jù)綁定和復(fù)雜數(shù)據(jù)綁定迅诬,接下來的幾個小節(jié)中腋逆,就對這兩種類型數(shù)據(jù)綁定進(jìn)行詳細(xì)講解。

簡單數(shù)據(jù)綁定
  • 綁定默認(rèn)數(shù)據(jù)類型

當(dāng)前端請求的參數(shù)比較簡單時侈贷,可以在后臺方法的形參中直接使用 Spring MVC 提供的默認(rèn)參數(shù)類型進(jìn)行數(shù)據(jù)綁定惩歉。
常用的默認(rèn)參數(shù)類型如下。

  • HttpServletRequest: 通過 request 對象獲取請求信息俏蛮。
  • HttpServletResponse : 通過 response 處理響應(yīng)信息撑蚌。
  • HttpSession: 通過 sesslon 對象得到 sesslon 中存儲的對象。
  • Model/ModeIMap: Model 是一個接口搏屑, ModelMap 是一個接口實現(xiàn)争涌,作用是將 model 數(shù)據(jù)填充到 request 域。

針對以上幾種默認(rèn)參數(shù)類型的數(shù)據(jù)綁定辣恋,本節(jié)將以 HttpServletRequest 類型的使用為例進(jìn)行演示說明亮垫,其具體步驟如下。
( 1 )在 Eclipse 中伟骨,創(chuàng)建一個名為 springmvc03 的 Web 項目饮潦,然后將 Spring MVC 相關(guān) JAR 包添加到項目的 lib 目錄下,并發(fā)布到類路徑中携狭。 添加 JAR 包后的 lib 目錄如圖所示继蜡。



( 2 ) 在 web.xml 中,配置 Spring MVC 的前端控制器等信息。
( 3 ) 在 src 目錄下稀并,創(chuàng)建 Spring MVC 的核心配置文件 springmvc-contig.xml 鲫剿,在該文件中配置組件掃描器和視圖解析器,文件如下所示稻轨。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
   http://www.springframework.org/schema/context 
   http://www.springframework.org/schema/context/spring-context-4.3.xsd">
  <!-- 定義組件掃描器灵莲,指定需要掃描的包  -->
  <context:component-scan base-package="com.neuedu.controller" />
  <!-- 定義視圖解析器  -->
  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <!-- 設(shè)置前綴  -->
      <property name="prefix" value="/WEB-INF/jsp/" /> 
      <!-- 設(shè)置后綴  -->
      <property name="suffix" value=".jsp" /> 
  </bean>
</beans>

( 4 )在 src 目錄下 ,創(chuàng)建一個 com.neuedu.controller 包殴俱,在該包下創(chuàng)建一個用于用戶操作的控制器類 UserController政冻,編寫后的代碼,文件如下所示线欲。

package com.neuedu.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class UserController {
  @RequestMapping("/selectUser") 
  public String selectUser(HttpServletRequest request){
      String id = request.getParameter("id");
      System.out.println("id = " + id);
      return "success";
  }
}

在文件中明场,使用注解方式定義了一個控制器類,同時定義了方法的訪問路徑李丰。 在方法參數(shù)中使用了 HttpServletRequest 類型苦锨,并通過該對象的 getParameter()方法獲取了指定的參數(shù)。 為了方便查看結(jié)果趴泌,將獲取的參數(shù)進(jìn)行輸出打印舟舒,最后返回一個名為 success 的視圖, Spring MVC 會通過視圖解析器在:
"/WEB-INF/jsp/"路徑下尋找 success.jsp 文件嗜憔。
小提示:后臺在編寫控制器類時秃励,通常會根據(jù)需要操作的業(yè)務(wù)對控制器類進(jìn)行規(guī)范命名。 例如要編寫一個 對用戶操作的控制器類吉捶,可以將控制器類命名為 UserController夺鲜,然后在該控制器類中就可以編寫任何 有關(guān)用戶操作的方法。
( 5 )在 WEB-INF 目錄下呐舔,創(chuàng)建一個名為 jsp 的文件夾币励,然后在該文件夾中創(chuàng)建頁面文件 success.jsp 丽柿, 該界面只作為正確執(zhí)行操作后的響應(yīng)頁面岖圈,沒有其他業(yè)務(wù)邏輯,文件如下所示贩虾。

<%@ 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>結(jié)果頁面</title>
</head>
<body>
  ok.....
</body>
</html>

( 6 ) 將 springmvc03 項目發(fā)布到 Tomcat 服務(wù)器并啟動 杆麸,在瀏覽器中訪問地址 http://localhost:8888/springmvc03/selectUser?id=1 搁进,其顯示效果如圖所示浪感。


此時的控制臺打印信息如圖所示昔头。

從圖中可以看出,后臺方法已從請求中正確地獲取到了 id 的參數(shù)信息影兽,這說明使用默認(rèn)的 HttpServletRequest 參數(shù)類型已經(jīng)完成了數(shù)據(jù)綁定揭斧。

  • 綁定簡單數(shù)據(jù)類型

簡單數(shù)據(jù)類型的綁定,就是指 Java 中幾種基本數(shù)據(jù)類型的綁定,如 int讹开、 String盅视、 Double 等類型。 這里仍然以前面小節(jié)中的參數(shù) id 為 1 的請求為例旦万,來講解簡單數(shù)據(jù)類型的綁定闹击。
首先修改控制器類,將控制器類 UserController 中的 selectUser() 方法的參數(shù)修改為使用簡單數(shù)據(jù)類型的形式成艘,修改后的代碼如下赏半。

  @RequestMapping("/selectUser") 
  public String selectUser(Integer id){
      System.out.println("id = " + id);
      return "success";
  }

與默認(rèn)數(shù)據(jù)類型案例中的 selectUser()方法相比,此方法中只是將 HttpServletRequest 參數(shù)類型替換為了 Integer 類型淆两。
啟動項目断箫,并在瀏覽器中訪問地址:http://localhost:8888/springmvc03/selectUser?id=1 ,會發(fā)現(xiàn)瀏覽器同樣正確跳轉(zhuǎn)到了 success.jsp 頁面秋冰,此時再查看控制臺打印信息仲义,如圖所示。


從圖中可以看出剑勾,使用簡單的數(shù)據(jù)類型同樣完成了數(shù)據(jù)綁定埃撵。
需要注意的是,有時候前端請求中參數(shù)名和后臺控制器類方法中的形參名不一樣虽另,這就會導(dǎo)致后臺無法正確綁定并接收到前端請求的參數(shù)盯另。 為此, Spring MVC 提供了@RequestParam 注解來進(jìn)行間接數(shù)據(jù)綁定洲赵。
@RequestParam 注解主要用于對請求中的參數(shù)進(jìn)行定義鸳惯,在使用時可以指定它的 4 個屬性, 具體如下表所示叠萍。

屬性 說明
value name 屬性的別名芝发,這里指參數(shù)的名字,即入?yún)⒌恼埱髤?shù)名字苛谷,如 value="item id"表示請求的參數(shù)中名字為 item id 的參數(shù)的值將傳入辅鲸。 如果只使用 vaule 屬性,則可以省略 value 屬性名
name 指定請求頭綁定的名稱
required 用于指定參數(shù)是否必須腹殿,默認(rèn)是 true独悴,表示請求中一定要有相應(yīng)的參數(shù)
defaultValue 默認(rèn)值,表示如果請求中沒茍罔名參數(shù)時的默認(rèn)值

@RequestParam 注解的使用非常簡單锣尉,假設(shè)瀏覽器中的請求地址為 http://localhost:8888/springmvc03/selectUser?user_id=1 刻炒,那么在后臺 selectUser() 方法中的使用方式如下。

  @RequestMapping("/selectUser") 
  public String selectUser(@RequestParam(value="user_id")Integer id){
      System.out.println("id = " + id);
      return "success";
  }

上述代碼會將請求中 user_id 參數(shù)的值 1 賦給方法中的 id 形參自沧。這樣通過輸出語句就可以輸出 id 形參中的值坟奥。

  • 綁定 POJO 類型

在使用簡單數(shù)據(jù)類型綁定時,可以很容易地根據(jù)具體需求來定義方法中的形參類型和個數(shù), 然而在實際應(yīng)用中爱谁,客戶端請求可能會傳遞多個不同類型的參數(shù)數(shù)據(jù)晒喷,如果還使用簡單數(shù)據(jù)類型進(jìn)行綁定,那么就需要手動編寫多個不同類型的參數(shù)访敌,這種操作顯然比較煩瑣凉敲。 此時就可以使用 POJO 類型進(jìn)行數(shù)據(jù)綁定。
POJO 類型的數(shù)據(jù)綁定就是將所有關(guān)聯(lián)的請求參數(shù)封裝在一個 POJO 中寺旺,然后在方法中直接使用該 POJO 作為形參來完成數(shù)據(jù)綁定荡陷。
接下來通過一個用戶注冊案例,來演示 POJO 類型數(shù)據(jù)的綁定迅涮,具體實現(xiàn)步驟如下废赞。
( 1 )在 src 目錄下,創(chuàng)建一個 com.neuedu.po 包叮姑,在該包下創(chuàng)建一個 User 類來封裝用戶注冊的信息參數(shù)唉地,編輯后文件如下所示。

package com.neuedu.po;
/**
* 用戶 POJO 類
*/
public class User {
  private Integer id; //用戶id
  private String username; //用戶
  private Integer password; //用戶密碼
  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 Integer getPassword() {
      return password;
  }
  public void setPassword(Integer password) {
      this.password = password;
  }
}

( 2 )在控制器 UserController 類中传透,編寫接收用戶注冊信息和向注冊頁面跳轉(zhuǎn)的方法耘沼,代碼如下所示。

  /**
   * 向用戶注冊頁面跳轉(zhuǎn)
   */
  @RequestMapping("/toRegister") 
  public String toReguster(){
      return "register";
  }
  /**
   * 接收用戶注冊信息
   */
  @RequestMapping("/registerUser") 
  public String registerUser(User user){
      String username = user.getUsername();
      Integer password = user.getPassword();
      System.out.println("username = "+username);
      System.out.println("password = "+password);
      return "success";
  }

( 3 )在/WEB-INF/jsp 目錄下朱盐,創(chuàng)建一個用戶注冊頁面 register.jsp 群嗤,在該界面中編寫用戶注冊表單,表單需要以 POST 方式提交兵琳,并且在提交時會發(fā)送一條以"/registerUser" 結(jié)尾的請求消息狂秘,文件如下所示。

<%@ 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>注冊</title>
</head>
<body>
  <form action="${pageContext.request.contextPath }/registerUser" method="post">
      用戶名:<input type="text" name="username" /><br>
      密&nbsp;碼:<input type="text" name="password" /><br>
      <input type="submit" value="注冊">
  </form>
</body>
</html>

注意:在使用 POJO 類型數(shù)據(jù)綁定時躯肌,前端請求的參數(shù)名(本例中指 form 表單內(nèi)各元素的 name 屬性值) 必須與要綁定的 POJO 類中的屬性名一樣者春,這樣才會自動將請求數(shù)據(jù)綁定到 POJO 對象中,否則后臺接收的參數(shù)值為 null清女。
( 4 )將項目發(fā)布到 Tomcat 服務(wù)器并啟動钱烟,在瀏覽器中訪問地址 http://localhost:8080/springmvc03/toRegister,就會跳轉(zhuǎn)到用戶注冊頁面register.jsp 嫡丙,如圖所示拴袭。


在圖中,填寫對應(yīng)的用戶名和密碼曙博,然后單擊"注冊"按鈕即可完成模擬注冊功能拥刻。 這里假設(shè)用戶注冊的用戶名和密碼分別為 "tom" 和 "123" ,當(dāng)單擊"注冊"按鈕后羊瘩,瀏覽器會跳轉(zhuǎn)到結(jié)果頁面泰佳,此時控制臺的輸出結(jié)果如圖所示盼砍。

從圖中可以看出尘吗,使用 POJO 類型同樣可以獲取前端請求傳遞過來的數(shù)據(jù)信息逝她,這就是 POJO 類型的數(shù)據(jù)綁定。

  • 多學(xué)一招:解決請求參數(shù)中的中文問題
    在前端請求中睬捶,難免會有中文信息傳遞黔宛,例如在前面圖中的用戶名和密碼輸入柜中輸入用戶名"小雪"和密碼 "123" 時,雖然瀏覽器可以正確跳轉(zhuǎn)到結(jié)果頁面擒贸,但是在控制臺中輸出的中文信息卻出現(xiàn)了亂碼臀晃,如圖所示。



    從圖可以看到介劫,密碼信息已正確顯示徽惋,但用戶名卻顯示為亂碼, 為了防止前端傳入的中文數(shù)據(jù)出現(xiàn)亂碼問題座韵,我們可以使用 Spring 提供的編碼過濾器來統(tǒng) 一編碼险绘。要使用編碼過濾器,只需要在 web.xml 中添加如下代碼誉碴。

      <!-- 配置編碼過濾器  -->
      <filter>
          <filter-name>CharacterEncodingFilter</filter-name>
          <filter-class>
              org.springframework.web.filter.CharacterEncodingFilter 
          </filter-class>
          <init-param>
              <param-name>encoding</param-name>
              <param-value>UTF-8</param-value>
          </init-param>
      </filter>
      <filter-mapping>
          <filter-name>CharacterEncodingFilter</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>

上述代碼中宦棺,通過<filter-mapping>元素的配直會攔截前端頁面中的所有請求,并交由名稱 為 CharacterEncodingFilter 的編碼過濾器類進(jìn)行處理黔帕。 在<filter>元素中代咸,首先配直了編碼過濾器類 org.springframework.web.filter.CharacterEncodingFilter, 然后通過初始化參數(shù)設(shè)置統(tǒng)一的編碼為 UTF-8成黄。 這樣所有的請求信息內(nèi)容都會以 UTF-8 的編碼格式進(jìn)行解析呐芥。 配直完成后,再次在頁面中輸入中文用戶名"小雪"以及密碼 "123" 奋岁,此時控制臺的打印信息如圖所示贩耐。



從圖中可以看出,控制臺中已經(jīng)正確顯示出了中文數(shù)據(jù)厦取,這說明編碼過濾器配置成功 潮太。

  • 綁定包裝 POJO

使用簡單 POJO 類型已經(jīng)可以完成多數(shù)的數(shù)據(jù)綁定,但有時客戶端請求中傳遞的參數(shù)會比較復(fù)雜虾攻。 例如铡买,在用戶查詢訂單時,頁面?zhèn)鬟f的參數(shù)可能包括訂單編號霎箍、用戶名稱等信息奇钞,這就包含了訂單和用戶兩個對象的信息。 如果將訂單和用戶的所有查詢條件都封裝在一個簡單 POJO 中漂坏,顯然會比較混亂景埃,這時就可以考慮使用包裝 POJO 類型的數(shù)據(jù)綁定媒至。
所謂的包裝 POJO,就是在一個 POJO 中包含另一個簡單 POJO谷徙。 例如拒啰,在訂單對象中包含用戶對象。 這樣在使用時完慧,就可以通過訂單查詢到用戶信息谋旦。
下面通過一個訂單查詢的案例,來演示包裝 POJO 數(shù)據(jù)綁定的使用屈尼,具體步驟如下册着。
( 1 )在 springmvc03 項目的 com.neuedu.po 包中,創(chuàng)建一個訂單類 Orders 脾歧,該類用于封裝訂單和用戶信息甲捏,編輯后文件如下所示。

package com.neuedu.po;
/**
* 訂單 POJO
*/
public class Orders {
  private Integer ordersId; //訂單編號
  private User user; //用戶POJO鞭执,所屬用戶
  public Integer getOrdersId() {
      return ordersId;
  }
  public void setOrdersId(Integer ordersId) {
      this.ordersId = ordersId;
  }
  public User getUser() {
      return user;
  }
  public void setUser(User user) {
      this.user = user;
  }
}

在上述包裝 POJO 類中司顿,定義了訂單號和用戶 POJO 的屬性及其對應(yīng)的 getter/setter 方法。 這樣訂單類中就不僅可以封裝訂單的基本屬性參數(shù)蚕冬,還可以封裝 User 類型的屬性參數(shù)免猾。
( 2 )在 com.neuedu.controller 包中,創(chuàng)建一個訂單控制器類 OrdersController囤热,在該類中編 寫一個跳轉(zhuǎn)到訂單查詢頁面的方法和一個查詢訂單及用戶信息的方法猎提,文件如下所示。

package com.neuedu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.neuedu.po.Orders;
import com.neuedu.po.User;
@Controller
public class OrdersController {
  /**
   * 向訂單查詢頁面跳轉(zhuǎn)
   */
  @RequestMapping("tofindOrdersWithUser")
  public String tofindOrdersWithUser(){
      return "orders";
  }
  /**
   * 查詢訂單和用戶信息
   */
  @RequestMapping("findOrdersWithUser")
  public String findOrdersWithUser(Orders orders){
      Integer ordersId = orders.getOrdersId();
      User user = orders.getUser();
      String username = user.getUsername();
      System.out.println("ordersId = "+ordersId);
      System.out.println("username = "+username);
      return "success";
  }
}

在文件中旁蔼,通過訪問頁面跳轉(zhuǎn)方法即可跳轉(zhuǎn)到 orders.jsp 中锨苏,而通過查詢訂單和用戶信息方法,即可通過傳遞的參數(shù)條件去調(diào)用 Service 中的相應(yīng)方法來查詢數(shù)據(jù)棺聊。 這里只是為了講解包裝 POJO 的使用伞租,所以只需將傳遞過來的參數(shù)進(jìn)行輸出 。
( 3 )在/WEB-INF/jsp 目錄下限佩,創(chuàng)建一個用戶訂單查詢頁面 orders.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>訂單查詢</title>
</head>
<body>
  <form action=" ${pageContext.request.contextPath }/findOrdersWithUser" method="post">
      訂單編號:<input type="text" name="ordersId" /><br>
      所屬用戶:<input type="text" name="user.username" /><br>
      <input type="submit" value="查詢">
  </form>
</body>
</html>
  • 注意
    在使用包裝 POJO 類型數(shù)據(jù)綁定時作喘,前端請求的參數(shù)名編寫必須符合以下兩種情況。
    ① 如果查詢條件參數(shù)是包裝類的直接基本屬性晕城,則參數(shù)名直接用對應(yīng)的屬性名泞坦,如上面代碼中的ordersld。
    ② 如果查詢條件參數(shù)是包裝類中 POJO 的子屬性砖顷,則參數(shù)名必須為【對象屬性】贰锁,其中【對象】要和包裝 POJO 中的對象屬性名稱一致赃梧, 【屬性】要和包裝 POJO 中的對象子屬性一致,如上述代碼中 的 user.username豌熄。

( 4 )將 springmvc03 項目發(fā)布到 Tomcat 服務(wù)器并啟動授嘀,在瀏覽器中訪問地址 http://localhost:8080/springmvc03/tofindOrdersWithUser ,其顯示效果如圖所示房轿。


在上圖中粤攒,填寫訂單編號為 "321654"所森,所屬用戶屬于"小韓"囱持,單擊"查詢"按鈕后,瀏覽器會跳轉(zhuǎn)到 success.jsp 頁面焕济,此時控制臺中的打印信息如圖所示纷妆。

從圖中可以看出,使用包裝 POJO 同樣完成了數(shù)據(jù)綁定晴弃。

  • 自定義數(shù)據(jù)綁定

在一般情況下掩幢,使用基本數(shù)據(jù)類型和 POJO 類型的參數(shù)數(shù)據(jù)已經(jīng)能夠滿足需求,然而有些特殊類型的參數(shù)是無法在后臺進(jìn)行直接轉(zhuǎn)換的上鞠,例如日期數(shù)據(jù)就需要開發(fā)者自定義轉(zhuǎn)換器 ( Converter )或格式化( Formatter )來進(jìn)行數(shù)據(jù)綁定际邻。

  1. Converter
    Spring 框架提供了一個 Converter 用于將一種類型的對象轉(zhuǎn)換為另一種類型的對象。 例如芍阎, 用戶輸入的曰期形式可能是 "2019-08-23" 或 "2019/08/23" 的字符串世曾,而要 Spring 將輸入的日期與后臺的 Date 進(jìn)行綁定,則需要將字符串轉(zhuǎn)換為日期谴咸,此時就可以自定義一個 Converter 類來進(jìn)行曰期轉(zhuǎn)換轮听。 自定義 Converter類需要實現(xiàn)org.springframework.core.convert.converter.Converter接口, 該接口的代碼如下所示岭佳。
public interface Converter<S,T> {
  T converter(S source);
}

在上述接口代碼中血巍,泛型中的 S 表示源類型, T 表示目標(biāo)類型珊随,而 convert(S source)表示接口中的方法述寡。
在 src 目錄下,創(chuàng)建一個 com.neuedu.convert 包叶洞,在該包下創(chuàng)建日期轉(zhuǎn)換類 DateConverter鲫凶, 并在該類中編寫將 String 類型轉(zhuǎn)換成 Date 類型的代碼,文件如下所示京办。

package com.neuedu.convert;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.core.convert.converter.Converter;
public class DateConverter implements Converter<String,Date> {
  //定義日期格式
  private String datePattern = "yyyy-MM-dd HH:mm:ss";
  @Override
  public Date convert(String source) {
      //格式化日期
      SimpleDateFormat sdf = new SimpleDateFormat(datePattern);
      try {
          return sdf.parse(source);
      } catch (ParseException e) {
          throw new IllegalArgumentException("無效的日期格式掀序,請使用這種格式:"+datePattern);
      }
  }   
}

在上述代碼中, DateConverter 類實現(xiàn)了 Converter 接口惭婿,該接口中第一個類型 String 表示需要被轉(zhuǎn)換的數(shù)據(jù)類型 不恭,第二個類型 Date 表示需要轉(zhuǎn)換成的目標(biāo)類型叶雹。
為了讓 Spring MVC 知道并使用這個轉(zhuǎn)換器類,還需要在其配置文件中編寫一個 id 為 conversionService 的 Bean 换吧,文件配置所示折晦。

  <!-- 顯示的裝配自定義類型轉(zhuǎn)換器  -->
  <mvc:annotation-driven conversion-service="conversionService"/> 
  <!-- 自定義類型轉(zhuǎn)換器  -->
  <bean id="conversionService"    
class="org.springframework.context.support.ConversionServiceFactoryBean">
      <property name="converters">
          <set>
              <bean class="com.neuedu.convert.DateConverter" />
          </set>
      </property>
  </bean>

在文件中,首先添加了 3 個 mvc 的 schema 信息 ;然后定義了組件掃描器和視圖解析器;接下來顯示裝配了自定義的類型轉(zhuǎn)換器;最后編寫了自定義類型轉(zhuǎn)換器的配置沾瓦,其中 Bean 的類名稱必須為 org.springframework.context.support.ConversionServiceFactoryBean 满着,并且 Bean 中還需要包含一個 converters 屬性,通過該屬性列出程序中自定義的所有 Converter贯莺。
為了測試轉(zhuǎn)換器類的使用风喇,可以在 com.neuedu.controller 包中創(chuàng)建一個日期控制器類 DateController,并在類中編寫綁定日期數(shù)據(jù)的方法缕探,文件如下所示魂莫。

package com.neuedu.controller;
import java.util.Date;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 日期控制器類
*/
@Controller
public class DateController {
  /**
   * 使用自定義類型數(shù)據(jù)綁定日期數(shù)據(jù)
   */
  @RequestMapping("/customDate") 
  public String CustomDate(Date date){
      System.out.println("date = "+date);
      return "success";
  }
}

此時,如果發(fā)布項目并啟動 Tomcat 服務(wù)器爹耗,在瀏覽器中訪問地址 http://localhost:8888/springmvc03/customDate?date=2019-08-23 15:55:55 (注意曰期數(shù)據(jù)中的空格)耙考,控制臺的打印信息如圖所示。


從圖中可以看出潭兽,使用自定義類型轉(zhuǎn)換器已從請求中正確獲取到了曰期信息倦始,這就是自定義數(shù)據(jù)綁定。

  1. Formatter
    除了使用 Converter 進(jìn)行轉(zhuǎn)換外山卦,我們還可以使用 Formatter 來進(jìn)行類型轉(zhuǎn)換鞋邑。 Formatter 與 Converter 的作用相同,只是 Formatter 的源類型必須是一個 String 類型怒坯,而 Converter 可以是任意類型炫狱。 使用 Formatter 自定義轉(zhuǎn)換器類需要實現(xiàn) org.springframework.format.Formatter 接口,該接口的代碼如下所示剔猿。
public interface Formatter<T> extends Printer<T>, Parser<T> {} 

在上述代碼中视译, Formatter 接口繼承了 Printer 和 Parser 接口,其泛型 T 表示輸入字符串要轉(zhuǎn)換的目標(biāo)類型归敬。 在 Printer 和 Parser 接口中酷含,分別包含一個 print() 和 parse() 方法,所有的實現(xiàn)類必須覆蓋這兩個方法汪茧。 在 com.neuedu.convert 包中椅亚,創(chuàng)建日期轉(zhuǎn)換類 DateFormatter,在該類中使用 Formatter 自定義日期轉(zhuǎn)換器舱污,文件如下所示呀舔。

package com.neuedu.convert;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.springframework.format.Formatter;
/**
* 使用Formatter自定義日期轉(zhuǎn)換器
*/
public class DateFormatter implements Formatter<Date>{
  //定義日期格式
  private String datePattern = "yyyy-MM-dd HH:mm:ss";
  //聲明SimpleDateFormat對象
  private SimpleDateFormat simpleDateFormat;
  @Override
  public String print(Date date, Locale locale) {
      return new SimpleDateFormat().format(date);
  }
  @Override
  public Date parse(String source, Locale locale) throws ParseException {
      simpleDateFormat = new SimpleDateFormat(datePattern);
      return simpleDateFormat.parse(source);
  }
}

在文件中, DateFormatter 類實現(xiàn)了 Formatter 接口扩灯,并實現(xiàn)了接口中的兩個方法媚赖。 其中 print() 方法會返回目標(biāo)對象的字符串霜瘪,而 parse() 方法會利用指定的 Locale 將一個 String 解析成目標(biāo)類型。 要使用 Formatter 自定義的曰期轉(zhuǎn)換器惧磺,同樣需要在 Spring MVC 的配置文件中進(jìn)行注冊颖对, 其配置代碼如下所示。

  <!-- 自定義類型格式化轉(zhuǎn)換器配置  --> 
  <bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
      <set>
          <bean class="com.neuedu.convert.DateFormatter" />
      </set>
  </bean>

與注冊 Converter 類有所不同的是磨隘,注冊自定義的 Formatter 轉(zhuǎn)換器類時缤底, Bean 的類名必須是 org.springframework.format.support.FormattingConversionServiceFactoryBean ,并且其屬性為 formatters番捂。 完成后个唧,通過地址 http://localhost:8080/springmvc03/customDate?date= 2019-08-23 15:55:55 即可查看實現(xiàn)的效果。 由于與前面的顯示結(jié)果相同白嘁,這里不再做演示坑鱼,大家可自行測試膘流。

復(fù)雜數(shù)據(jù)綁定

在學(xué)習(xí)完前面小節(jié)講解的簡單數(shù)據(jù)綁定后絮缅,大家已經(jīng)能夠完成實際開發(fā)中多數(shù)的數(shù)據(jù)綁定問題,但仍可能遇到一些比較復(fù)雜的數(shù)據(jù)綁定問題呼股,比如數(shù)組的綁定耕魄、集合的綁定,這在實際開發(fā)中也是十分常見的彭谁。 接下來的兩個小節(jié)中吸奴,將具體講解一下數(shù)組綁定和集合綁定的使用。

  • 綁定數(shù)組

在實際開發(fā)時缠局,可能會遇到前端請求需要傳遞到后臺一個或多個相同名稱參數(shù)的情況 (如批量刪除)则奥,此種情況采用前面講解的簡單數(shù)據(jù)綁定的方式顯然是不合適的。 此時狭园,就可以使用綁定數(shù)組的方式读处,來完成實際需求。
下面通過一個批量刪除用戶的例子來詳細(xì)講解綁定數(shù)組的操作 唱矛,其具體實現(xiàn)步驟如下罚舱。
( 1 )在 springmvc03 項目的/WEB-INF/jsp 目錄下,創(chuàng)建一個展示用戶信息的列表頁面 user.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>用戶列表</title>
</head>
<body>
  <form action="${pageContext.request.contextPath }/deleteUsers">
      <table width="20%" border="1">
          <tr>
              <th>選擇</th>
              <th>用戶名</th>
          </tr>
          <tr>
              <td>
                  <input type="checkbox" name="ids" value="1">
              </td>
              <td>tom</td>
          </tr>
          <tr>
              <td>
                  <input type="checkbox" name="ids" value="2">
              </td>
              <td>jack</td>
          </tr>
          <tr>
              <td>
                  <input type="checkbox" name="ids" value="3">
              </td>
              <td>lucy</td>
          </tr>
      </table>
      <input type="submit" value="刪除">
  </form>
</body>
</html>

在上述頁面代碼中,定義了 3 個 name 屬性相同而 value 屬性值不同的復(fù)選框控件窃肠,并在每 一個復(fù)選框?qū)?yīng)的行中編寫了一個對應(yīng)用戶包个。 在單擊"刪除"按鈕執(zhí)行刪除操作時,表單會提交到一個以 "/deleteUsers" 結(jié)尾的請求中冤留。
( 2 )在控制器類 UserController 中碧囊,編寫接收批量刪除用戶的方法 ( 同時為了方便向用戶列表頁面跳轉(zhuǎn)恃锉,還需增加一個向 user.jsp 頁面跳轉(zhuǎn)的方法),其代碼如下所示呕臂。

  /**
   * 向用戶列表頁面跳轉(zhuǎn)
   */
  @RequestMapping ("/toUser") 
  public String selectUsers(){
      return "user";
  }
  /**
   * 接收批量刪除用戶的方法
   */
  @RequestMapping ("/deleteUsers") 
  public String deleteUsers(Integer[] ids){
      if(ids != null){
          for (Integer id : ids) {
              System.out.println("刪除了id為"+id+"的用戶破托!");
          }
      }else{
          System.out.println("ids = null");
      }
      return "success";
  }

在上述代碼中,先定義了一個向用戶列表頁面 user.jsp 跳轉(zhuǎn)的方法歧蒋,然后定義了一個接收前端批量刪除用戶的方法土砂。 在刪除方法中,使用了 Integer 類型的數(shù)組進(jìn)行數(shù)據(jù)綁定谜洽,并通過 for 循環(huán)執(zhí)行具體數(shù)據(jù)的刪除操作萝映。
( 3 )發(fā)布項目到 Tomcat 服務(wù)器并啟動后,在瀏覽器中訪問地址 http://localhost:8080/springmvc03/toUser 阐虚,其顯示效果如圖所示.序臂。


勾選圖中的全部復(fù)選框,然后單擊"刪除"按鈕实束,這樣程序在正確執(zhí)行后會跳轉(zhuǎn)到 success.jsp 頁面奥秆。 此時控制臺的打印信息,如圖所示咸灿。

從圖中可以看出构订,已成功執(zhí)行了批量刪除操作,這就說明已成功實現(xiàn)了數(shù)組類型的數(shù)據(jù)綁定避矢。

  • 綁定集合

在批量刪除用戶的操作中悼瘾,前端請求傳遞的都是同名參數(shù)的用戶 id ,只要在后臺使用同一種數(shù)組類型的參數(shù)綁定接收审胸,就可以在方法中通過循環(huán)數(shù)組參數(shù)的方式來完成刪除操作亥宿。 但如果是批量修改用戶操作,前端請求傳遞過來的數(shù)據(jù)可能就會批量包含各種類型的數(shù)據(jù)砂沛,如 Integer烫扼、 String 等。 這種情況使用數(shù)組綁定是無法實現(xiàn)的尺上,那么我們應(yīng)該怎么做呢?
針對這種情況材蛛,我們可以使用集合數(shù)據(jù)綁定。 即在包裝類中定義一個包含用戶信息類的集合怎抛, 然后在接收方法中將參數(shù)類型定義為該包裝類的集合卑吭。
下面就以批量修改用戶為例,來講解一下集合數(shù)據(jù)綁定的使用马绝,具體實現(xiàn)步驟如下豆赏。
( 1 )在 src 目錄下,創(chuàng)建一個 com.neuedu.vo 包,并在包中創(chuàng)建包裝類 UserVO 來封裝用戶集合屬性及編輯后的代碼文件如下所示掷邦。

package com.neuedu.po;
import java.util.List;
/**
* 用戶包裝類
*/
public class UserVO {
  private List<User> users;
  public List<User> getUsers() {
      return users;
  }
  public void setUsers(List<User> users) {
      this.users = users;
  }   
}

在上述代碼中白胀,聲明了一個List<User>類型的集合屬性 users ,并編寫了該屬性對應(yīng)的 getter/setter 方法抚岗。 該集合屬性就是用于綁定批量修改用戶的數(shù)據(jù)信息或杠。
( 2 )在控制器類 UserController 中,編寫接收批量修改用戶的方法宣蔚,以及向用戶修改頁面跳轉(zhuǎn)的方法向抢,其代碼如下所示。

  /**
   * 向用戶批量修改頁面跳轉(zhuǎn)
   */
  @RequestMapping("/toUserEdit") 
  public String toUserEdit(){
      return "user_edit";
  }
  /**
   * 接收批量修改用戶的方法
   */
  @RequestMapping("/editUsers") 
  public String editUsers(UserVO userList){
      //將所有用戶數(shù)據(jù)封裝到集合中
      List<User> users = userList.getUsers();
      //循環(huán)輸出所有用戶信息
      for (User user : users) {
          //如果接收的用戶 id 不為空胚委,則表示對該用戶進(jìn)行了修改
          if(user.getId() != null){
              System.out.println("修改了id為:"+user.getId()+"的用戶名為:"+user.getUsername());
          }
      }
      return "success";
  }

在上述代碼的兩個方法中挟鸠,通過 toUserEdit() 方法將跳轉(zhuǎn)到 user_edit.jsp 頁面,通過 editUsers() 方法將執(zhí)行用戶批量更新操作亩冬,其中 editUsers() 方法的 UserVO 類型參數(shù)用于綁定并獲取頁面?zhèn)鬟f過來的用戶數(shù)據(jù)艘希。
注意:在使用集合數(shù)據(jù)綁定時,后臺方法中不支持直接使用集合形參進(jìn)行數(shù)據(jù)綁定硅急,所以需要使用包裝 POJO 作為形參覆享,然后在包裝 POJO 中包裝一個集合屬性。
( 3 )在項目的/WEB-INF/jsp 目錄下 铜秆,創(chuàng)建頁面文件 user_edit.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>修改用戶</title>
</head>
<body>
  <form action="${pageContext.request.contextPath }/editUsers" method="post" id='formid'>
      <table width="30%" border=1>
          <tr>
              <th>選擇</th>
              <th>用戶名</th>
          </tr>
          <tr>
              <td>
                  <input type="checkbox" name="users[0].id" value="1">
              </td>
              <td>
                  <input type="text" value="tome" name="users[0].username" >
              </td>
          </tr>
          <tr>
              <td>
                  <input type="checkbox" name="users[1].id" value="2">
              </td>
              <td>
                  <input type="text" value="jack" name="users[1].username" >
              </td>
          </tr>
      </table>
      <input type="submit" value="修改">
  </form>
</body>
</html>

在上述頁面代碼中,模擬展示了 id 為 1 巍糯、用戶名為 tome 和 id 為 2啸驯、用戶名為 jack 的兩個 用戶。 當(dāng)單擊"修改"按鈕后祟峦,會將表單提交到一個以 "/editUsers" 結(jié)尾的請求中罚斗。
( 4 )發(fā)布項目到 Tomcat 服務(wù)器并啟動后,在瀏覽器中訪問地址 http://localhost:8880/springmvc03/toUserEdit 宅楞,其顯示效果如圖所示针姿。


將圖中的用戶名 tome 改為 tom , jack 改為 jacks 厌衙,并勾選兩個數(shù)據(jù)前面的復(fù)選框距淫, 然后單擊"修改"按鈕后,瀏覽器會跳轉(zhuǎn)到 success.jsp 頁面中婶希。此時控制臺的打印信息如圖所示榕暇。

從圖中可以看出,已經(jīng)成功輸出了請求中批量修改的用戶信息,這就是集合類型的數(shù)據(jù)綁定彤枢。

本章小結(jié)

本章主要對 Spring MVC 中的數(shù)據(jù)綁定進(jìn)行了詳細(xì)講解狰晚。 首先講解了簡單的數(shù)據(jù)綁定,包括 默認(rèn)數(shù)據(jù)類型缴啡、簡單數(shù)據(jù)類型壁晒、 POJO 類型、包裝 POJO 類型以及自定義參數(shù)類型綁定;然后講解了復(fù)雜數(shù)據(jù)綁定业栅,包括數(shù)組類型讨衣、集合類型綁定。通過本章的學(xué)習(xí)式镐,大家能夠熟練地掌握 Spring MVC 中幾種數(shù)據(jù)類型的綁定使用反镇,這將為后續(xù)的學(xué)習(xí)打下堅實的基礎(chǔ)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末娘汞,一起剝皮案震驚了整個濱河市歹茶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌你弦,老刑警劉巖惊豺,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異禽作,居然都是意外死亡尸昧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門旷偿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來烹俗,“玉大人,你說我怎么就攤上這事萍程〈蓖” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵茫负,是天一觀的道長蕉鸳。 經(jīng)常有香客問我,道長忍法,這世上最難降的妖魔是什么潮尝? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮饿序,結(jié)果婚禮上勉失,老公的妹妹穿的比我還像新娘。我一直安慰自己嗤堰,他們只是感情好戴质,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布度宦。 她就那樣靜靜地躺著,像睡著了一般告匠。 火紅的嫁衣襯著肌膚如雪戈抄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天后专,我揣著相機(jī)與錄音划鸽,去河邊找鬼。 笑死戚哎,一個胖子當(dāng)著我的面吹牛裸诽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播型凳,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼丈冬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了甘畅?” 一聲冷哼從身側(cè)響起埂蕊,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疏唾,沒想到半個月后蓄氧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡槐脏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年喉童,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顿天。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡堂氯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出露氮,到底是詐尸還是另有隱情祖灰,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布畔规,位于F島的核電站,受9級特大地震影響恨统,放射性物質(zhì)發(fā)生泄漏叁扫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一畜埋、第九天 我趴在偏房一處隱蔽的房頂上張望莫绣。 院中可真熱鬧,春花似錦悠鞍、人聲如沸对室。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掩宜。三九已至蔫骂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牺汤,已是汗流浹背辽旋。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留檐迟,地道東北人补胚。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓喉磁,卻偏偏與公主長得像澎胡,于是被迫代替她去往敵國和親放闺。 傳聞我的和親對象是個殘疾皇子嵌器,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 懶得處理樣式了, 將就著看吧. 官網(wǎng)地址: https://developer.android.com/topic...
    Reddington_604e閱讀 1,667評論 0 1
  • 1. 簡介 1.1 什么是 MyBatis 隧哮? MyBatis 是支持定制化 SQL攀甚、存儲過程以及高級映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,523評論 0 4
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,105評論 1 32
  • 張春紅網(wǎng)絡(luò)初級十期堅持原創(chuàng)分享129天 20180710剛剛結(jié)束的初級十期第五次課上看得出劉老師很疲憊苔严,也難怪役纹,有...
    純粹記錄閱讀 56評論 0 0
  • 嘉許兄弟每瞒,運動減脂取得階段性很好的效果金闽,啤酒肚已經(jīng)沒了,繼續(xù)加油剿骨。 真的代芜,不比不知道,一比嚇一跳浓利,效果是如此的明顯...
    張攀鋒007er閱讀 564評論 0 1