通过第 12 章的学习,大家已经知道后台的请求处理方法可以包含多种参数类型。 在实际的项目开发中,多数情况下客户端会传递带有不同参数的请求,那么后台是如何绑定并获取这些请求参数的呢?那么接下来,本章将对 Spring MVC 框架中的数据绑定进行详细讲解。
数据绑定介绍
在执行程序时, Spring MVC 会根据客户端请求参数的不同,将请求消息中的信息以一定的方式转换并绑定到控制器类的方法参数中 。 这种将请求消息数据与后台方法参数建立连接的过程就是 Spring MVC 中的数据绑定。
在数据绑定过程中, Spring MVC 框架会通过数据绑定组件( DataBinder )将请求参数串的内容进行类型转换,然后将转换后的值赋给控制器类中方法的形参,这样后台方法就可以正确绑定并获取客户端请求携带的参数了。 整个数据绑定的过程如图所示。
关于上图中信息处理过程的步骤描述如下。
( 1 ) Spring MVC 将 ServletRequest 对象传递给 DataBinder。
( 2 )将处理方法的入参对象传递给 DataBinder。
( 3 ) DataBinder 调用 ConversionService 组件进行数据类型转换、数据格式化等工作,并 将 ServletRequest 对象中的消息填充到参数对象中 。
( 4 )调用 Validator 组件对已经绑定了请求消息数据的参数对象进行数据合法性校验。
( 5 )校验完成后会生成数据绑定结果 BindingResult 对象, Spring MVC 会将 BindingResult 对象中的内容赋给处理方法的相应参数。
根据客户端请求参数类型和个数的不同,我们将 Spring MVC 中的数据绑定主要分为简单数据绑定和复杂数据绑定,接下来的几个小节中,就对这两种类型数据绑定进行详细讲解。
简单数据绑定
- 绑定默认数据类型
当前端请求的参数比较简单时,可以在后台方法的形参中直接使用 Spring MVC 提供的默认参数类型进行数据绑定。
常用的默认参数类型如下。
- HttpServletRequest: 通过 request 对象获取请求信息。
- HttpServletResponse : 通过 response 处理响应信息。
- HttpSession: 通过 sesslon 对象得到 sesslon 中存储的对象。
- Model/ModeIMap: Model 是一个接口, ModelMap 是一个接口实现,作用是将 model 数据填充到 request 域。
针对以上几种默认参数类型的数据绑定,本节将以 HttpServletRequest 类型的使用为例进行演示说明,其具体步骤如下。
( 1 )在 Eclipse 中,创建一个名为 springmvc03 的 Web 项目,然后将 Spring MVC 相关 JAR 包添加到项目的 lib 目录下,并发布到类路径中。 添加 JAR 包后的 lib 目录如图所示。
( 2 ) 在 web.xml 中,配置 Spring MVC 的前端控制器等信息。
( 3 ) 在 src 目录下,创建 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"> <!-- 设置前缀 --> <property name="prefix" value="/WEB-INF/jsp/" /> <!-- 设置后缀 --> <property name="suffix" value=".jsp" /> </bean> </beans>
( 4 )在 src 目录下 ,创建一个 com.neuedu.controller 包,在该包下创建一个用于用户操作的控制器类 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"; } }
在文件中,使用注解方式定义了一个控制器类,同时定义了方法的访问路径。 在方法参数中使用了 HttpServletRequest 类型,并通过该对象的 getParameter()方法获取了指定的参数。 为了方便查看结果,将获取的参数进行输出打印,最后返回一个名为 success 的视图, Spring MVC 会通过视图解析器在:
"/WEB-INF/jsp/"路径下寻找 success.jsp 文件。
小提示:后台在编写控制器类时,通常会根据需要操作的业务对控制器类进行规范命名。 例如要编写一个 对用户操作的控制器类,可以将控制器类命名为 UserController,然后在该控制器类中就可以编写任何 有关用户操作的方法。
( 5 )在 WEB-INF 目录下,创建一个名为 jsp 的文件夹,然后在该文件夹中创建页面文件 success.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> ok..... </body> </html>
( 6 ) 将 springmvc03 项目发布到 Tomcat 服务器并启动 ,在浏览器中访问地址 http://localhost:8888/springmvc03/selectUser?id=1 ,其显示效果如图所示。
此时的控制台打印信息如图所示。
从图中可以看出,后台方法已从请求中正确地获取到了 id 的参数信息,这说明使用默认的 HttpServletRequest 参数类型已经完成了数据绑定。
- 绑定简单数据类型
简单数据类型的绑定,就是指 Java 中几种基本数据类型的绑定,如 int、 String、 Double 等类型。 这里仍然以前面小节中的参数 id 为 1 的请求为例,来讲解简单数据类型的绑定。
首先修改控制器类,将控制器类 UserController 中的 selectUser() 方法的参数修改为使用简单数据类型的形式,修改后的代码如下。@RequestMapping("/selectUser") public String selectUser(Integer id){ System.out.println("id = " + id); return "success"; }
与默认数据类型案例中的 selectUser()方法相比,此方法中只是将 HttpServletRequest 参数类型替换为了 Integer 类型。
启动项目,并在浏览器中访问地址:http://localhost:8888/springmvc03/selectUser?id=1 ,会发现浏览器同样正确跳转到了 success.jsp 页面,此时再查看控制台打印信息,如图所示。
从图中可以看出,使用简单的数据类型同样完成了数据绑定。
需要注意的是,有时候前端请求中参数名和后台控制器类方法中的形参名不一样,这就会导致后台无法正确绑定并接收到前端请求的参数。 为此, Spring MVC 提供了@RequestParam 注解来进行间接数据绑定。
@RequestParam 注解主要用于对请求中的参数进行定义,在使用时可以指定它的 4 个属性, 具体如下表所示。
属性 说明 value name 属性的别名,这里指参数的名字,即入参的请求参数名字,如 value="item id"表示请求的参数中名字为 item id 的参数的值将传入。 如果只使用 vaule 属性,则可以省略 value 属性名 name 指定请求头绑定的名称 required 用于指定参数是否必须,默认是 true,表示请求中一定要有相应的参数 defaultValue 默认值,表示如果请求中没苟罔名参数时的默认值 @RequestParam 注解的使用非常简单,假设浏览器中的请求地址为 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 参数的值 1 赋给方法中的 id 形参。这样通过输出语句就可以输出 id 形参中的值。
- 绑定 POJO 类型
在使用简单数据类型绑定时,可以很容易地根据具体需求来定义方法中的形参类型和个数, 然而在实际应用中,客户端请求可能会传递多个不同类型的参数数据,如果还使用简单数据类型进行绑定,那么就需要手动编写多个不同类型的参数,这种操作显然比较烦琐。 此时就可以使用 POJO 类型进行数据绑定。
POJO 类型的数据绑定就是将所有关联的请求参数封装在一个 POJO 中,然后在方法中直接使用该 POJO 作为形参来完成数据绑定。
接下来通过一个用户注册案例,来演示 POJO 类型数据的绑定,具体实现步骤如下。
( 1 )在 src 目录下,创建一个 com.neuedu.po 包,在该包下创建一个 User 类来封装用户注册的信息参数,编辑后文件如下所示。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 类中,编写接收用户注册信息和向注册页面跳转的方法,代码如下所示。
/** * 向用户注册页面跳转 */ @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 目录下,创建一个用户注册页面 register.jsp ,在该界面中编写用户注册表单,表单需要以 POST 方式提交,并且在提交时会发送一条以"/registerUser" 结尾的请求消息,文件如下所示。
<%@ 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> 密 码:<input type="text" name="password" /><br> <input type="submit" value="注册"> </form> </body> </html>
注意:在使用 POJO 类型数据绑定时,前端请求的参数名(本例中指 form 表单内各元素的 name 属性值) 必须与要绑定的 POJO 类中的属性名一样,这样才会自动将请求数据绑定到 POJO 对象中,否则后台接收的参数值为 null。
( 4 )将项目发布到 Tomcat 服务器并启动,在浏览器中访问地址 http://localhost:8080/springmvc03/toRegister,就会跳转到用户注册页面register.jsp ,如图所示。
在图中,填写对应的用户名和密码,然后单击"注册"按钮即可完成模拟注册功能。 这里假设用户注册的用户名和密码分别为 "tom" 和 "123" ,当单击"注册"按钮后,浏览器会跳转到结果页面,此时控制台的输出结果如图所示。
从图中可以看出,使用 POJO 类型同样可以获取前端请求传递过来的数据信息,这就是 POJO 类型的数据绑定。
多学一招:解决请求参数中的中文问题
在前端请求中,难免会有中文信息传递,例如在前面图中的用户名和密码输入柜中输入用户名"小雪"和密码 "123" 时,虽然浏览器可以正确跳转到结果页面,但是在控制台中输出的中文信息却出现了乱码,如图所示。
从图可以看到,密码信息已正确显示,但用户名却显示为乱码, 为了防止前端传入的中文数据出现乱码问题,我们可以使用 Spring 提供的编码过滤器来统 一编码。要使用编码过滤器,只需要在 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 的编码过滤器类进行处理。 在<filter>元素中,首先配直了编码过滤器类 org.springframework.web.filter.CharacterEncodingFilter, 然后通过初始化参数设置统一的编码为 UTF-8。 这样所有的请求信息内容都会以 UTF-8 的编码格式进行解析。 配直完成后,再次在页面中输入中文用户名"小雪"以及密码 "123" ,此时控制台的打印信息如图所示。
从图中可以看出,控制台中已经正确显示出了中文数据,这说明编码过滤器配置成功 。
- 绑定包装 POJO
使用简单 POJO 类型已经可以完成多数的数据绑定,但有时客户端请求中传递的参数会比较复杂。 例如,在用户查询订单时,页面传递的参数可能包括订单编号、用户名称等信息,这就包含了订单和用户两个对象的信息。 如果将订单和用户的所有查询条件都封装在一个简单 POJO 中,显然会比较混乱,这时就可以考虑使用包装 POJO 类型的数据绑定。
所谓的包装 POJO,就是在一个 POJO 中包含另一个简单 POJO。 例如,在订单对象中包含用户对象。 这样在使用时,就可以通过订单查询到用户信息。
下面通过一个订单查询的案例,来演示包装 POJO 数据绑定的使用,具体步骤如下。
( 1 )在 springmvc03 项目的 com.neuedu.po 包中,创建一个订单类 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 的属性及其对应的 getter/setter 方法。 这样订单类中就不仅可以封装订单的基本属性参数,还可以封装 User 类型的属性参数。
( 2 )在 com.neuedu.controller 包中,创建一个订单控制器类 OrdersController,在该类中编 写一个跳转到订单查询页面的方法和一个查询订单及用户信息的方法,文件如下所示。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 { /** * 向订单查询页面跳转 */ @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"; } }
在文件中,通过访问页面跳转方法即可跳转到 orders.jsp 中,而通过查询订单和用户信息方法,即可通过传递的参数条件去调用 Service 中的相应方法来查询数据。 这里只是为了讲解包装 POJO 的使用,所以只需将传递过来的参数进行输出 。
( 3 )在/WEB-INF/jsp 目录下,创建一个用户订单查询页面 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 类型数据绑定时,前端请求的参数名编写必须符合以下两种情况。
① 如果查询条件参数是包装类的直接基本属性,则参数名直接用对应的属性名,如上面代码中的ordersld。
② 如果查询条件参数是包装类中 POJO 的子属性,则参数名必须为【对象属性】,其中【对象】要和包装 POJO 中的对象属性名称一致, 【属性】要和包装 POJO 中的对象子属性一致,如上述代码中 的 user.username。( 4 )将 springmvc03 项目发布到 Tomcat 服务器并启动,在浏览器中访问地址 http://localhost:8080/springmvc03/tofindOrdersWithUser ,其显示效果如图所示。
在上图中,填写订单编号为 "321654",所属用户属于"小韩",单击"查询"按钮后,浏览器会跳转到 success.jsp 页面,此时控制台中的打印信息如图所示。
从图中可以看出,使用包装 POJO 同样完成了数据绑定。
- 自定义数据绑定
在一般情况下,使用基本数据类型和 POJO 类型的参数数据已经能够满足需求,然而有些特殊类型的参数是无法在后台进行直接转换的,例如日期数据就需要开发者自定义转换器 ( Converter )或格式化( Formatter )来进行数据绑定。
- Converter
Spring 框架提供了一个 Converter 用于将一种类型的对象转换为另一种类型的对象。 例如, 用户输入的曰期形式可能是 "2019-08-23" 或 "2019/08/23" 的字符串,而要 Spring 将输入的日期与后台的 Date 进行绑定,则需要将字符串转换为日期,此时就可以自定义一个 Converter 类来进行曰期转换。 自定义 Converter类需要实现org.springframework.core.convert.converter.Converter接口, 该接口的代码如下所示。public interface Converter<S,T> { T converter(S source); }
在上述接口代码中,泛型中的 S 表示源类型, T 表示目标类型,而 convert(S source)表示接口中的方法。
在 src 目录下,创建一个 com.neuedu.convert 包,在该包下创建日期转换类 DateConverter, 并在该类中编写将 String 类型转换成 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 类实现了 Converter 接口,该接口中第一个类型 String 表示需要被转换的数据类型 ,第二个类型 Date 表示需要转换成的目标类型。
为了让 Spring MVC 知道并使用这个转换器类,还需要在其配置文件中编写一个 id 为 conversionService 的 Bean ,文件配置所示。<!-- 显示的装配自定义类型转换器 --> <mvc:annotation-driven conversion-service="conversionService"/> <!-- 自定义类型转换器 --> <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 信息 ;然后定义了组件扫描器和视图解析器;接下来显示装配了自定义的类型转换器;最后编写了自定义类型转换器的配置,其中 Bean 的类名称必须为 org.springframework.context.support.ConversionServiceFactoryBean ,并且 Bean 中还需要包含一个 converters 属性,通过该属性列出程序中自定义的所有 Converter。
为了测试转换器类的使用,可以在 com.neuedu.controller 包中创建一个日期控制器类 DateController,并在类中编写绑定日期数据的方法,文件如下所示。package com.neuedu.controller; import java.util.Date; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /** * 日期控制器类 */ @Controller public class DateController { /** * 使用自定义类型数据绑定日期数据 */ @RequestMapping("/customDate") public String CustomDate(Date date){ System.out.println("date = "+date); return "success"; } }
此时,如果发布项目并启动 Tomcat 服务器,在浏览器中访问地址 http://localhost:8888/springmvc03/customDate?date=2019-08-23 15:55:55 (注意曰期数据中的空格),控制台的打印信息如图所示。
从图中可以看出,使用自定义类型转换器已从请求中正确获取到了曰期信息,这就是自定义数据绑定。
- Formatter
除了使用 Converter 进行转换外,我们还可以使用 Formatter 来进行类型转换。 Formatter 与 Converter 的作用相同,只是 Formatter 的源类型必须是一个 String 类型,而 Converter 可以是任意类型。 使用 Formatter 自定义转换器类需要实现 org.springframework.format.Formatter 接口,该接口的代码如下所示。public interface Formatter<T> extends Printer<T>, Parser<T> {}
在上述代码中, Formatter 接口继承了 Printer 和 Parser 接口,其泛型 T 表示输入字符串要转换的目标类型。 在 Printer 和 Parser 接口中,分别包含一个 print() 和 parse() 方法,所有的实现类必须覆盖这两个方法。 在 com.neuedu.convert 包中,创建日期转换类 DateFormatter,在该类中使用 Formatter 自定义日期转换器,文件如下所示。
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自定义日期转换器 */ 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 类实现了 Formatter 接口,并实现了接口中的两个方法。 其中 print() 方法会返回目标对象的字符串,而 parse() 方法会利用指定的 Locale 将一个 String 解析成目标类型。 要使用 Formatter 自定义的曰期转换器,同样需要在 Spring MVC 的配置文件中进行注册, 其配置代码如下所示。
<!-- 自定义类型格式化转换器配置 --> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <set> <bean class="com.neuedu.convert.DateFormatter" /> </set> </bean>
与注册 Converter 类有所不同的是,注册自定义的 Formatter 转换器类时, Bean 的类名必须是 org.springframework.format.support.FormattingConversionServiceFactoryBean ,并且其属性为 formatters。 完成后,通过地址 http://localhost:8080/springmvc03/customDate?date= 2019-08-23 15:55:55 即可查看实现的效果。 由于与前面的显示结果相同,这里不再做演示,大家可自行测试。
复杂数据绑定
在学习完前面小节讲解的简单数据绑定后,大家已经能够完成实际开发中多数的数据绑定问题,但仍可能遇到一些比较复杂的数据绑定问题,比如数组的绑定、集合的绑定,这在实际开发中也是十分常见的。 接下来的两个小节中,将具体讲解一下数组绑定和集合绑定的使用。
- 绑定数组
在实际开发时,可能会遇到前端请求需要传递到后台一个或多个相同名称参数的情况 (如批量删除),此种情况采用前面讲解的简单数据绑定的方式显然是不合适的。 此时,就可以使用绑定数组的方式,来完成实际需求。
下面通过一个批量删除用户的例子来详细讲解绑定数组的操作 ,其具体实现步骤如下。
( 1 )在 springmvc03 项目的/WEB-INF/jsp 目录下,创建一个展示用户信息的列表页面 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 属性值不同的复选框控件,并在每 一个复选框对应的行中编写了一个对应用户。 在单击"删除"按钮执行删除操作时,表单会提交到一个以 "/deleteUsers" 结尾的请求中。
( 2 )在控制器类 UserController 中,编写接收批量删除用户的方法 ( 同时为了方便向用户列表页面跳转,还需增加一个向 user.jsp 页面跳转的方法),其代码如下所示。/** * 向用户列表页面跳转 */ @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 跳转的方法,然后定义了一个接收前端批量删除用户的方法。 在删除方法中,使用了 Integer 类型的数组进行数据绑定,并通过 for 循环执行具体数据的删除操作。
( 3 )发布项目到 Tomcat 服务器并启动后,在浏览器中访问地址 http://localhost:8080/springmvc03/toUser ,其显示效果如图所示.。
勾选图中的全部复选框,然后单击"删除"按钮,这样程序在正确执行后会跳转到 success.jsp 页面。 此时控制台的打印信息,如图所示。
从图中可以看出,已成功执行了批量删除操作,这就说明已成功实现了数组类型的数据绑定。
- 绑定集合
在批量删除用户的操作中,前端请求传递的都是同名参数的用户 id ,只要在后台使用同一种数组类型的参数绑定接收,就可以在方法中通过循环数组参数的方式来完成删除操作。 但如果是批量修改用户操作,前端请求传递过来的数据可能就会批量包含各种类型的数据,如 Integer、 String 等。 这种情况使用数组绑定是无法实现的,那么我们应该怎么做呢?
针对这种情况,我们可以使用集合数据绑定。 即在包装类中定义一个包含用户信息类的集合, 然后在接收方法中将参数类型定义为该包装类的集合。
下面就以批量修改用户为例,来讲解一下集合数据绑定的使用,具体实现步骤如下。
( 1 )在 src 目录下,创建一个 com.neuedu.vo 包,并在包中创建包装类 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 ,并编写了该属性对应的 getter/setter 方法。 该集合属性就是用于绑定批量修改用户的数据信息。
( 2 )在控制器类 UserController 中,编写接收批量修改用户的方法,以及向用户修改页面跳转的方法,其代码如下所示。/** * 向用户批量修改页面跳转 */ @RequestMapping("/toUserEdit") public String toUserEdit(){ return "user_edit"; } /** * 接收批量修改用户的方法 */ @RequestMapping("/editUsers") public String editUsers(UserVO userList){ //将所有用户数据封装到集合中 List<User> users = userList.getUsers(); //循环输出所有用户信息 for (User user : users) { //如果接收的用户 id 不为空,则表示对该用户进行了修改 if(user.getId() != null){ System.out.println("修改了id为:"+user.getId()+"的用户名为:"+user.getUsername()); } } return "success"; }
在上述代码的两个方法中,通过 toUserEdit() 方法将跳转到 user_edit.jsp 页面,通过 editUsers() 方法将执行用户批量更新操作,其中 editUsers() 方法的 UserVO 类型参数用于绑定并获取页面传递过来的用户数据。
注意:在使用集合数据绑定时,后台方法中不支持直接使用集合形参进行数据绑定,所以需要使用包装 POJO 作为形参,然后在包装 POJO 中包装一个集合属性。
( 3 )在项目的/WEB-INF/jsp 目录下 ,创建页面文件 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 的两个 用户。 当单击"修改"按钮后,会将表单提交到一个以 "/editUsers" 结尾的请求中。
( 4 )发布项目到 Tomcat 服务器并启动后,在浏览器中访问地址 http://localhost:8880/springmvc03/toUserEdit ,其显示效果如图所示。
将图中的用户名 tome 改为 tom , jack 改为 jacks ,并勾选两个数据前面的复选框, 然后单击"修改"按钮后,浏览器会跳转到 success.jsp 页面中。此时控制台的打印信息如图所示。
从图中可以看出,已经成功输出了请求中批量修改的用户信息,这就是集合类型的数据绑定。
本章小结
本章主要对 Spring MVC 中的数据绑定进行了详细讲解。 首先讲解了简单的数据绑定,包括 默认数据类型、简单数据类型、 POJO 类型、包装 POJO 类型以及自定义参数类型绑定;然后讲解了复杂数据绑定,包括数组类型、集合类型绑定。通过本章的学习,大家能够熟练地掌握 Spring MVC 中几种数据类型的绑定使用,这将为后续的学习打下坚实的基础。