SpringMVC 学习笔记(八)- 数据转换 & 数据校验 & 数据格式化
1. 数据绑定流程
- Spring MVC 主框架将ServletRequest 对象目标方法的入参实例传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象
- DataBinder 调用装配在 Spring MVC 上下文中的 ConversionService 组件进行数据类型转换、数据格式化工作。将 Servlet 中的请求信息填充到入参对象中
- 调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果 BindingData 对象
- Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参
Spring MVC 通过反射机制对目标方法进行解析,将请求绑定到处理方法的入参中。数据绑定的核心部件是 DataBinder,运行机制如下:
2. 自定义类型转换器(了解)
注意:本次实例在 CRUD 代码中加入
1. 在视图 input.jsp 中加入
<form action="testConversionServiceConverer" method="POST">
<!-- lastname-email-gender-department.id 例如: [email protected] -->
Employee: <input type="text" name="employee"/>
<input type="submit" value="Submit"/>
</form>
<br><br>
2. 在 src 下创建处理器类 SpringMVCTest
@Controller
public class SpringMVCTest {
@Autowired
private EmployeeDao employeeDao;
@RequestMapping("/testConversionServiceConverer")
public String testConverter(@RequestParam("employee") Employee employee){
System.out.println("save: " + employee);
employeeDao.save(employee);
return "redirect:/emps";
}
}
3. 在 src 下创建自定义转换器 EmployeeConverter
package www.xq.springmvc.converters;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import www.xq.springmvc.crud.entities.Department;
import www.xq.springmvc.crud.entities.Employee;
@Component
public class EmployeeConverter implements Converter<String, Employee> {
@Override
public Employee convert(String source) {
if(source != null){
String [] vals = source.split("-");
//[email protected]
if(vals != null && vals.length == 4){
String lastName = vals[0];
String email = vals[1];
Integer gender = Integer.parseInt(vals[2]);
Department department = new Department();
department.setId(Integer.parseInt(vals[3]));
Employee employee = new Employee(null, lastName, email, gender, department);
System.out.println(source + "--convert--" + employee);
return employee;
}
}
return null;
}
}
4. 在 springMVC 的配置文件中配置转换器
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<!-- 配置 ConversionService -->
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="employeeConverter"/>
</set>
</property>
</bean>
5. 运行结果
前台
后台
[email protected] [id=null, lastName=GG, [email protected], gender=0, department=Department [id=105, departmentName=null], birth=null, salary=null]
save: Employee [id=null, lastName=GG, [email protected], gender=0, department=Department [id=105, departmentName=null], birth=null, salary=null]
3. 关于 mvc:annotation-driven
- mvc:annotation-driven/ 会自动注册 RequestMappingHandlerMapping、RequestMappingHandlerAdapter 与 ExceptionHandlerExceptionResolver 三个 bean。
- 还将提供以下支持:
- 支持使用 ConversionService 实例对表单参数进行类型转换
- 支持使用 @NumberFormat annotation、@DateTimeFormat 注解完成数据类型的格式化
- 支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证
- 支持使用 @RequestBody 和 @ResponseBody 注解
4. @InitBinder 注解
1. 简介
- 由 @InitBinder 标识的方法,可以对 WebDataBinder 对象进行初始化。WebDataBinder 是 DataBinder 的子类,用于完成由表单字段到 JavaBean 属性的绑定
- @InitBinder 方法不能有返回值,它必须声明为 void。
- @InitBinder 方法的参数通常是 WebDataBinder
2. 举例
1. 处理器类 EmployeeHandler 加入方法
@InitBinder
public void initBinder(WebDataBinder binder){
//不能设置lastName的值
binder.setDisallowedFields("lastName");
}
2. 测试添加
运行结果
5. 数据格式化
1. 简介
- 对属性对象的输入/输出进行格式化,从其本质上讲依然属于“类型转换的范畴”
- Spring 在格式化模块中定义了一个实现 ConversionService 接口的 FormattingConversionService 实现类,该实现类扩展了 GenericConversionService,因此它既具有类型转换的功能,又具有格式化的功能,具体实现如下图:
- FormattingConversionService 拥有一个 FormattingConversionServiceFactoryBean 工厂类,后者用于在 Sring 上下文中构造前者(具体例子如下图)
2. 举例(只需在对应实例类的相关属性上加上注解即可)
1. 首先应该在 springMVC 配置文件中装配 FormattingConversionServiceFactoryBean 后,就可以在 SpringMVC 入参绑定及模型数据输出时使用注解驱动了。
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<!-- 配置 ConversionService -->
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="employeeConverter"/>
</set>
</property>
</bean>
2. 具体
日期格式化
//示例输入:1999-09-09
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birth;
数值格式化
//示例输入:1,525,555.4
@NumberFormat(pattern="#,###,###.#")
private Float salary;
3. 数据格式化出错设置
if(result.getErrorCount() > 0){
System.out.println("出错了!");
for(FieldError error:result.getFieldErrors()){
System.out.println(error.getField() + ":" + error.getDefaultMessage());
}
//若验证出错, 则转向定制的页面
map.put("departments", departmentDao.getDepartments());
return "input";
}
6. JSR 303 数据校验
1. 如何校验 ? 注解 ?
1. 使用 JSR 303 验证标准
2. 加入 hibernate validator 验证框架的 jar 包
3. 在 SpringMVC 配置文件中添加 <mvc:annotation-driven />
4. 需要在 bean 的属性上添加对应的注解
5. 在目标方法 bean 类型的前面添加 @Valid 注解
注意:假如出现 NoSuchMethodError 异常,那么可能是 el jar 包冲突,具体将 hibernate validator 中有关 el jar 包替换Tomcat中java目录下的 el jar包
2. 具体如下
1. 请求处理器类 EmployeeHandler save 方法加工
@RequestMapping(value="/emp", method=RequestMethod.POST)
public String save(@Valid Employee employee, Errors result,
Map<String, Object> map){
System.out.println("save: " + employee);
if(result.getErrorCount() > 0){
System.out.println("出错了!");
for(FieldError error:result.getFieldErrors()){
System.out.println(error.getField() + ":" + error.getDefaultMessage());
}
//若验证出错, 则转向定制的页面
map.put("departments", departmentDao.getDepartments());
return "input";
}
employeeDao.save(employee);
return "redirect:/emps";
}
2. bean 类加入对应注解
具体操作
@NotEmpty
private String lastName;
@Email
private String email;
@Past
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birth;
总结
- JSR 303
- JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 中
- JSR 303 通过在 Bean 属性上标注类似 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证
- Hibernate Validator 扩展注解
- Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解
3. 视图 input.jsp 加入错误标签以显示错误信息
<!-- 显示全部错误 -->
<form:errors path="*"></form:errors>
<!-- 显示 name 错误 -->
<form:errors path="lastName"></form:errors>
<!-- 显示 email 错误 -->
<form:errors path="email"></form:errors>
<!-- 显示 日期 错误 -->
<form:errors path="birth"></form:errors>
4. 运行结果
前台
后台
save: Employee [id=null, lastName=, [email protected], gender=0, department=Department [id=101, departmentName=null], birth=Fri Nov 22 00:00:00 CST 2019, salary=1111111.0]
出错了!
birth:需要是一个过去的事件
email:不是一个合法的电子邮件地址
lastName:不能为空