目录
(1)接下来通过一个用户注册案例,来演示POJO类型数据的绑定的使用:
一、 数据绑定介绍
1.什么是数据绑定?
在执行程序时,Spring MVC会根据客户端请求参数的不同,将请求消息中的信息以一定的方式转换并绑定到控制器类的方法参数中。这种将请求消息数据与后台方法参数建立连接的过程就是Spring MVC中的数据绑定。
2.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中的数据绑定主要分为简单数据绑定和复杂数据绑定,接下来的几个小节中,就对这两种类型数据绑定进行详细讲解。
1.绑定默认数据类型
当前端请求的参数比较简单时,可以在后台方法的形参中直接使用Spring MVC提供的默认参数类型进行数据绑定。常用默认参数类型:
- HttpServletRequest:通过request对象获取请求信息;
- HttpServletResponse:通过response处理响应信息;
- HttpSession:通过session对象得到session中存放的对象;
- Model/ModelMap:Model是一个接口,ModelMap是一个接口实现,作用是将model数据填充到request域。
接下来,以HttpServletRequest类型的使用为例,来演示默认数据类型绑定的使用:
(1)在Eclipse中,创建一个名为chapter13的 Web项目,然后将 Spring MVC相关JAR包添加到项目的lib目录下,并发布到类路径中。添加JAR包后的lib目录如图所示。
(2)在 web.xml中,配置 Spring MVC的前端控制器等信息。
(3)在src目录下,创建Spring MVC的核心配置文件springmvc-config.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<--指定需要扫描的包-->
<context:component-scan base-package="com.itheima.controller"/>
<--定义视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<--设置前缀-->
<property name="prefix" value="/WEB-INF/jsp/"></property>
<--设置后缀-->
<property name="suffix" value=".jsp"></property>
</bean></beans>
( 4)在src目录下,创建一个com.itheima.controller包,在该包下创建一个用于用户操作的控制器类UserController
@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 的视图,SpringMVC会通过视图解析器在“/WEB-INF/jsp/”路径下寻找success.jsp文件。
( 5)在WEB-INF目录下,创建一个名为 jsp 的文件夹,然后在该文件夹中创建页面文件success.jsp,该界面只作为正确执行操作后的响应页面,没有其他业务逻辑
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>结果页面</title>
</head>
<body>
ok
</body>
</html>
启动Web项目,访问http://localhost:8080/chapter13/selectUser?id=5 ,其效果显示图
从图可以看出,后台方法已从请求中正确地获取到了id的参数信息,这说明使用默闰的 HttpServletRequest参数类型已经完成了数据绑定。
2. 绑定简单数据类型
简单数据类型的绑定,就是指Java中几种基本数据类型的绑定,例如int、String、Double等类型。这里仍然以上一小节中的参数id为5的请求为例,来讲解简单数据类型的绑定。
将控制器类UserController中的selectUser()方法进行修改,将默认参数类型HttpServletRequest改用基本类型Integer绑定接收:
@RequestMapping("/selectUser")
public String selectUser(Integer id) {
System.out.println("id="+id);
return "success";
}
启动项目,并在浏览器中访问地址 http:/localhost:8080/chapter13/selectUser?id=1,会发现浏览器同样正确跳转到了success.jsp 页面。
这里需要注意的是,有时候前端请求中参数名和后台控制器类方法中的形参名不一样,这就会导致后台无法正确绑定并接收到前端请求的参数。针对上述提到的前端请求中参数名和后台控制器类方法中的形参名不一样的情况,可以考虑使用Spring MVC提供的@RequestParam注解类型来进行间接数据绑定。
@RequestParam注解的属性声明如下:
@RequestParam注解的使用非常简单,假设浏览器中的请求地址为http://localhost:8080/chapter13/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形参中的值。
3.绑定POJO类型
在使用简单数据类型绑定时,可以很容易的根据具体需求来定义方法中的形参类型和个数,然而在实际应用中,客户端请求可能会传递多个不同类型的参数数据,如果还使用简单数据类型进行绑定,那么就需要手动编写多个不同类型的参数,这种操作显然比较繁琐。
针对多类型、多参数的请求,可以使用POJO类型进行数据绑定。 POJO类型的数据绑定就是将所有关联的请求参数封装在一个POJO中,然后在方法中直接使用该POJO作为形参来完成数据绑定。
(1)接下来通过一个用户注册案例,来演示POJO类型数据的绑定的使用:
( 1 )在src目录下,创建一个com.itheima.po包,在该包下创建一个User类来封装用户注册的信息参数
public class User {
//主键id自增
private Integer id;
//姓名
private String username;
//密码
private String 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 String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}}
(2)在控制器UserController类中,编写接收用户注册信息和向注册页面跳转的方法,代码如下所示。
/**
* 用户注册页面跳转
*/
@RequestMapping("/toRegister")
public String toRegister() {
return "register";
}@RequestMapping("/registerUser")
public String register(User user) {
String username = user.getUsername();
String password = user.getPassword();
System.out.println(username);
System.out.println(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>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/registerUser" method="post">
用户名:<input type="text" name="username"></br>
密   码:<input type="password" name="password"></br>
<input type="submit" value="注册">
</form>
</body>
</html>
注意:在使用POJO类型数据绑定时,前端请求的参数名(本例中指form表单内各元素的name属性值)必须与要绑定的Pojo类中的属性名一样,这样才会自动将请求数据绑定到POJO对象中,否则后台接收的参数值为null。
(4)将项目发布到Tomcat服务器并启动,在浏览器中访问地址http://localhost:8080/chapter13/toRegister,就会跳转到用户注册页面register.jsp,如图所示。
在上图中,填写对应的用户名和密码,然后单击“注册”按钮即可完成模拟注册功能。这里假设用户注册的用户名和密码分别为“tom”和“123”,当单击“注册”按钮后,浏览器会跳转到结果页面,此时控制台的输出结果如图所示。
可以看出,使用POJO类型同样可以获取前端请求传递过来的数据信息,这就是POJO类型的数据绑定。
(2)解决请求参数中的中文乱码问题
在前端请求中,难免会有中文信息传递,此时后台方法绑定接收的中文信息却就会出现了中文乱码,如下图所示:
为了防止前端传入的中文数据出现乱码问题,我们可以在web.xml中配置Spring提供的编码过滤器来统一编码。
<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的编码格式进行解析。
4.绑定包装POJO
使用简单POJO类型已经可以完成多数的数据绑定,但有时客户端请求中传递的参数会比较复杂。例如,在用户查询订单时,页面传递的参数可能包括订单编号、用户名称等信息,这就包含了订单和用户两个对象的信息。如果将订单和用户的所有查询条件都封装在一个简单POJC中,显然会比较混乱,这时就可以考虑使用包装 POJO类型的数据绑定。
所谓的包装POJO,就是在一个POJO中包含另一个简单POJO。例如,在订单对象中包含用户对象。这样在使用时,就可以通过订单查询到用户信息。
下面通过一个订单查询的案例,来演示包装POJO数据绑定的使用,具体步骤如下。
(1)在chapter13项目的com.itheima.po包中,创建一个订单类Orders,该类用于封装订单和用户信息。
public class Orders {
private Integer orderId;
private User user;
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}}
这样订单类中就不仅可以封装订单的基本属性参数,还可以封装User类型的属性参数。
( 2)在com.itheima.controller包中,创建一个订单控制器类OrdersController,在该类中编写一个跳转到订单查询页面的方法和一个查询订单及用户信息的方法
@Controller
public class OrdersController {
/**
*跳转页面
*方法的作用就是跳转到一个对应页面
*/
@RequestMapping("/toFindOrdersWithUser")
public String toFindOrdersWithUser() {
return "orders";
}
/**
*获取用户查询订单输入的信息
*然后根据信息进行查询
*/
@RequestMapping("/findOrdersWithUser")
public String findOrdersWithUser(Orders orders) {
Integer orderId = orders.getOrderId();
String username = orders.getUser().getUsername();
System.out.println(orderId);
System.out.println(username);
return "success";}
}
(3)在/WEB-INF/jsp目录下,创建一个用户订单查询页面 orders.jsp,在页面中编写通过订单编号和所属用户作为查询条件来查询订单信息的代码
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/findOrdersWithUser" method="post">
订单编号:<input type="text" name="orderId"/><br>
所属用户:<input type="text" name="user.username"/><br>
<input type="submit" value="查询"/>
</form>
</body>
</html>
在使用包装POJO类型数据绑定时,前端请求的参数名编写必须符合以下两种情况。
- 如果查询条件参数是包装类的直接基本属性,则参数名直接用对应的属性名,如上面代码中的ordersld。
- 如果查询条件参数是包装类中PoJo的子属性,则参数名必须为【对象.属性】,其中【对象】要和包装POJO中的对象属性名称一致,【属性】要和包装POJo中的对象子属性一致,如上述代码中的user.username
( 4)将chapter13项目发布到Tomcat 服务器并启动,访问http://localhost:8080/chapter13/tofindOrdersWithUser;其效果显示如图
在图中,填写订单编号为“12345”,所属用户为“张三”,单击“查询”按钮后,浏览器会跳转到success.jsp页面,此时控制台中的打印信息如图所示。
5.自定义数据绑定
一般情况下,使用基本数据类型和POJO类型的参数数据已经能够满足需求,然而有些特殊类型的参数是无法在后台进行直接转换的,但也有特殊数据类型无法直接进行数据绑定,必须先经过数据转换,例如日期数据。
如何处理这种数据类型的请求呢?针对前面提到的特殊数据类型,就需要开发者自定义转换器(Converter)或格式化(Formatter)来进行数据绑定。
(1)Converter
Spring 框架提供了一个Converter用于将一种类型的对象转换为另一种类型的对象。例如,用户输入的日期形式可能是“2017-04-08”或“2017/04/08”的字符串,而要Spring 将输入的日期与后台的Date进行绑定,则需要将字符串转换为日期,此时就可以自定义一个Converter类来进行日期转换。
自定义Converter类需要实现org.springframework.core.convert.converter.Converter接口,该接口的代码如下:
public interface Converter<S, T> {
T convert(S source);
}
在上述接口代码中,泛型中的S表示源类型,T表示目标类型,而 convert(S source)表示接口中的方法。
(1)在src目录下,创建一个com.itheima.convert包,在该包下创建日期转换类DateConverter ,并在该类中编写将String类型转换成Date类型的代码
public class DateConverter implements Converter<String, Date>{
//定义一个日期的格式
private String datePatter = "yyyy-MM-dd HH:mm:ss";@Override
public Date convert(String source) {
//对日期进行格式化
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePatter);
try {
return simpleDateFormat.parse(source);
} catch (ParseException e) {
throw new IllegalArgumentException("无效的日期格式,请使用这种格式:"+datePatter);
}
}
}
(2)为了让 Spring MVC 知道并使用这个转换器类,还需要在其配置文件中编写一个 id 为conversionService的 Bean
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.itheima.controller"></context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--自定义类型转换器的配置 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.itheima.convert.DateConverter"></bean>
</set>
</property>
</bean>
<!--显示装配自定义类型转换器 -->
<mvc:annotation-driven conversion-service="conversionService"/>
</beans>
(3)为了测试转换器类的使用,可以在 com.itheima.controller包中创建一个日期控制器类DateController,并在类中编写绑定日期数据的方法
@Controller
public class DateController {
/**
*使用自定义类型数据绑定日期数据
*/
@RequestMapping("/customDate")
public String customDate(Date date) {
System.out.println(date);
return "success";
}}
(4)发布项目并启动Tomcat 服务器,在浏览器中访问地址 http:/localhost:8080/chapter13/customDate?date= 2017-04-1215:55:55(注意日期数据中的空格),控制台的打印信息如图所示。
(2)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()方法,所有的实现类必须覆盖这两个方法。
(1)在com.itheima.convert 包中,创建日期转换类DateFormatter,在该类中使用Formatter自定义日期转换器
public class DateFormatter implements Formatter<Date>{
//定义一个日期的格式
private String datePatter = "yyyy-MM-dd HH:mm:ss";
//对日期进行格式化
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(datePatter);
return simpleDateFormat.parse(source);
}}
在文件13-12中,DateFormatter类实现了Formatter接口,并实现了接口中的两个方法。其中 print()方法会返回目标对象的字符串,而parse()方法会利用指定的Locale将一个 String 解析成目标类型。
(2)要使用Formatter自定义的日期转换器,同样需要在Spring MVC的配置文件中进行注册,其配置代码如下所示。
<!--自定义类型转换器的配置 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="com.itheima.convert.DateFormatter"></bean>
</set>
</property>
</bean>
完成后,通过地址http:/localhost:8080/chapter13/customDate?date= 2017-04-1215:55:55即可查看实现的效果。由于显示结果相同,这里不再做演示,读者可自行测试。