1.类型转换
form表单提交的数据都是String类型,例如在Servlet中我们是通过String filedName=request.getParameter(“…”)方法来获取相应的字段值。如果需要的是int类型,在 Servlet中我们也必须进行类型转换,如int age =Integer.parseInt(…)。但是在SpringMVC中,我们并不需要关心类型的转换,例如:
@RequestMapping(value = "/requestWithREST/{id}",method = RequestMethod.POST)
public String requestWithRestAdd(@PathVariable("id") Integer id){
System.out.println("增加时需要的id:" + id);
return "success";
}
SpringMVC可以直接将form表单中的id字段值转为Integer类型,并传递给requestWithRestAdd()方法中的参数id。这是因为SpringMVC中存在着一些内置的类型转换器,可以自动实现大多数的类型转换。
除此以外,我们还可以根据需求来自定义类型转换器。例如,现在需要将form表单传来的字符串“1-张三-23”解析成学号为1、姓名为张三、年龄为23,并将这些值封装到一个学生对象之中,也就是说需要将字符串“1-张三-23”转换为Student对象类型。
以下是具体的实现步骤:
①创建自定义类型转换器
创建基于SpringMVC的自定义类型转换器,需要新建一个类,并实现SpringMVC提供的Converter接口,如下,
自定义类型转换器,用于将字符串转换为Student类型 : StudentConverter.java
public class StudentConverter implements Converter<String, Student>{
@Override
public Student convert(String source){
//source值就是前端form传来的"1-张三-23"
if (source != null){
//解析出source中的学号、姓名、年龄
String[] vals = source.split("-");
int stuNo = Integer.parseInt(vals[0]);
String stuName = vals[1];
int stuAge = Integer.parseInt(vals[2]);
//将解析出的学号、姓名、年龄封装到Student对象之中
Student student = new Student();
student.setStuNo(stuNo);
student.setStuAge(stuAge);
student.setStuName(stuName);
return student;
}
return null;
}
}
②注册自定义类型转换器
springmvc.xml:将自定义的类型转换器注册到SpringMVC之中,共三步
<!-- ①将自定义的类型转换器加入SpringIOC容器 -->
<bean id="studentConverter" class="org.lanqiao.converter.StudentConverter"></bean>
<!-- ②将自定义的类型转换器注册到 SpringMVC提供的ConversionServiceFactoryBean中-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="studentConverter"/>
</set>
</property>
</bean>
<!--③ 将自定义的类型转换器所在的ConversionServiceFactoryBean,
注册到annotation-driven之中 -->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
至此就完成了自定义类型转化器的编写及配置工作。以下,对配置完成的类型转换器StudentConverter进行测试。
③请求处理方法
FirstSpringDemo.java
@Controller
@RequestMapping(value = "/FirstSpringDemo")
public class FirstSpringDemo{
@RequestMapping("/testConversionServiceConverer")
public String testConversionServiceConverer(@RequestParam("studentInfo") Student student){
System.out.println(student);
return "success";
}
}
④测试
<form action="FirstSpringDemo/testConversionServiceConverer">
学生信息: <input type="text" name="studentInfo"/>
<input type="submit" value="增加"/>
</form>
输入学生信息“1-张三-23”,如图,
点击“增加”后,可在控制台得到以下结果:
通过自定义类型转换器StudentConverter,成功的将前端传来的字符串“1-张三-23”转为了请求处理方法参数中的Student类型。
2.格式化数据
有时候需要对于日期、数字等类型进行格式化操作,例如:规定日期的格式必须为yyyy-MM-dd。
使用SpringMVC实现数据的格式化,只需要简单的两步操作:
①在需要格式化的属性前加上格式化注解,如@DateTimeFormat;
②在springmvc.xml中加入mvc:annotation-driven</mvc:annotation-driven>和SpringMVC提供的FormattingConversionServiceFactoryBean,如下:
springmvc.xml
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
</bean>
说明:
通过类的名字可知,FormattingConversionServiceFactoryBean
既提供了格式化需要的“Formatting”,又提供了类型转换需要的“Conversion”。因此,之前配置类型转换时使用的ConversionServiceFactoryBean
,也可以使用FormattingConversionServiceFactoryBean
来替代。也就是说,使用以下配置既可以实现自定义的类型转换,也可以实现格式化数据:
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="studentConverter"/>
</set>
</property>
</bean>
例如,以下指定Date类型的birthday属性的输入格式必须为yyyy-MM-dd。
Student.java
public class Student {
private int stuNo;
private String stuName;
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birthday ;
//setter、getter
}
通过注解@DateTimeFormat(pattern="yyyy-MM-dd")
指定birthday属性的输入格式必须为yyyy-MM-dd。以下是测试格式化的操作:
请求处理方法:FirstSpringDemo.java
@Controller
@RequestMapping(value = "/FirstSpringDemo")
public class FirstSpringDemo{
@RequestMapping("/testDateTimeFormat")
public String testDateTimeFormat(Student student){
System.out.println(student);
return "success";
}
}
请求页index.jsp
<form action="FirstSpringDemo/testDateTimeFormat">
姓名:<input type="text" name="stuName"/><br>
年龄:<input type="text" name="stuAge"/><br>
生日:<input type="text" name="birthday"/><br>
<input type="submit" value="提交"/>
</form>
开发人员调试的时候,可以给请求处理方法加入一个BindingResult类型的参数,此参数就包含了格式化数据失败时的异常信息,如下:
FirstSpringDemo.java
@Controller
@RequestMapping(value = "/FirstSpringDemo")
public class FirstSpringDemo{
@RequestMapping("/testDateTimeFormat")
public String testDateTimeFormat(Student student, BindingResult result) {
//如果有错误信息
if (result.getErrorCount() > 0) {
//循环遍历所有错误信息
for (FieldError error : result.getFieldErrors()) {
System.out.println(error.getField() + ":" + error.getDefaultMessage());
}
}
return "success";
}
}
此时再输入不符合格式的日期“2015年05月16日”,就能既在JSP页面显示HTTP Status 400异常,又能在控制台得到具体的异常信息。
除了用于格式化日期的注解@DateTimeFormat以外,SpringMVC还提供了用于格式化数字的注解@NumberFormat,例如,可以使用@NumberFormat指定以下int类型的属性count的输入格式为“#,###”(其中#代表数字)
public class ClassName{
@NumberFormat(pattern="#,###")
private int count;
//setter、getter
}
通过form表单中的input字段来映射count属性时,合法输入:如1,234;不合法的输入:如12,34。
3.数据校验
除了使用JS、正则表达式以外,还可以使用JSR 303-Bean Validation(简称JSR 303)来实现数据的校验。例如:用户名不能为空,email必须是一个合法地址,某个日期时间必须在当前时间之前等众多校验,都可以使用JSR 303-Bean Validation非常方便的实现。
JSR 303通过在实体类的属性上标注类@NotNull、@Max等注解指定校验规则,并通过与注解相对应的验证接口(JSR303内置提供)对属性值进行验证。
JSR 303提供的标准注解如下:
注解 | :简介 |
@Null | 被注释的元素必须为 null。 |
@NotNull | 被注释的元素必须不为 null。 |
@AssertTrue | 被注释的元素必须为 true。 |
@AssertFalse | 被注释的元素必须为 false。 |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于或等于value。 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于或等于value。 |
@DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于或等于value。 |
@DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于或等于value。 |
@Size(max, min) | 被注释的元素的取值范围必须是介于min和max之间。 |
@Digits (integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内。 |
@Past | 被注释的元素必须是一个过去的日期。 |
@Future | 被注释的元素必须是一个将来的日期。 |
@Pattern(value) | 被注释的元素必须符合指定的正则表达式。 |
Hibernate Validator 是JSR 303的扩展。Hibernate Validator 提供了 JSR 303中所有内置的注解,以及自身扩展的4个注解,如下:
注解 | 简介 |
@Email | 被注释的元素值必须是合法的电子邮箱地址。 |
@Length | 被注释的字符串的长度必须在指定的范围内。 |
@NotEmpty | 被注释的字符串的必须非空。 |
@Range | 被注释的元素必须在合适的范围内。 |
以下是使用Spring整合Hibernate Validator实现数据校验的步骤:
①导入JAR包:
Spring整合Hibernate Validator共需要导入以下5个JAR包:
hibernate-validator-5.0.0.CR2.jar
classmate-0.8.0.jar j
boss-logging-3.1.1.GA.jar
validation-api-1.1.0.CR1.jar
hibernate-validator-annotation-processor-5.0.0.CR2.jar
②加入<mvc:annotation-driven/>
Spring提供了一个LocalValidatorFactoryBean
类,这个类既实现了Spring的校验接口,也实现了JSR303的校验接口。因此,Spring整合Hibernate Validator时,需要在Spring容器中定义了一个LocalValidatorFactoryBean。方便的是,<mvc:annotation-driven/>
就会自动给Spring容器装配一个LocalValidatorFactoryBean,因此只需要在springmvc.xml中配置上<mvc:annotation-driven/>
即可。
③使用JSR303或Hibernate Validator校验注解,标识实体类的属性:
Student.java
public class Student {
@Past
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birthday ;
@Email
private String email;
//setter、getter
}
规定birthday必须在当天之前、email必须符合邮箱格式。
④在请求处理方法对应的实体类参数前,增加@Valid注解
SpringMVC会对标有@Valid注解的实体类参数进行校验,并且可以通过BindingResult类型的参数来存储校验失败时的信息,如下:
请求处理类:FirstSpringDemo.java
@Controller
@RequestMapping(value = "/FirstSpringDemo")
public class FirstSpringDemo{
@RequestMapping("/testValid")
public String testValid(@Valid Student student, BindingResult result){
if (result.getErrorCount() > 0){
//循环遍历所有错误信息
for (FieldError error : result.getFieldErrors()) {
System.out.println(error.getField() + ":" + error.getDefaultMessage());
}
}
return "success";
}
}
⑤测试
index.jsp
<form action="FirstSpringDemo/testValid">
用户名:<input type="text" name="stuName"/><br>
生日:<input type="text" name="birthday"/><br>
邮箱:<input type="text" name="email"/><br>
<input type="submit" value="提交"/>
</form>
如果输入的数据不符合要求,点击提交后,就会在控制台得到校验失败的信息(错误信息是JSR303/Hibernate Validator框架提供的,无需开发人员编写),如果希望校验失败后,跳转到错误提示页面(error.jsp),可以通过以下方式实现:
请求处理类:FirstSpringDemo.java
@Controller
@RequestMapping(value = "/FirstSpringDemo")
public class FirstSpringDemo{
@RequestMapping("/testValid")
public String testValid(@Valid Student student, BindingResult result, Map<String, Object> map){
if (result.getErrorCount() > 0){
//将错误信息通过map放入request作用域之中
map.put ("errors",result.getFieldErrors());
return "error";
}
return "success";
}
}
错误提示页:error.jsp
<c:forEach items="${errors }" var="error">
${error.getDefaultMessage() }、
</c:forEach>
说明:
需要注意的是:在请求处理方法的参数中,实体类参数和存储错误信息的BindingResult参数必须书写在一起,它们之间不能掺杂任何其它参数。
例如,可以写成:
public String testValid(@Valid Student student, BindingResult result, Map<String, Object> map)
但不能写成:
public String testValid(@Valid Student student, Map<String, Object> map, BindingResult result)