springMVC整理05--数据校验、格式化 & 其他注解 & 数据绑定流程

时间:2024-01-20 13:38:03

1.  数据校验、数据格式化

springMVC整理05--数据校验、格式化 & 其他注解 & 数据绑定流程

参考博客 http://www.importnew.com/19477.html

1.1  数据校验

使用 spring 数据校验,先要导入校验器的 jar:

 <!--数据校验-->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.2.Final</version>
</dependency>

此处使用的 hibernate 校验器
JSR 规范:
在实体类的属性上添加注解,可以完成数据校验:
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内

@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
------------------------------
Hibernate Validator 附加的注解
@NotBlank(message =) 验证字符串非 null,且长度必须大于 0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内

如 Employee 实体类中:

 public class  Employee { {
private Integer id;
@NotEmpty( message = " " 用户名不能为空" ")
@Size( min = 3 3, max = 6 6, message = " " 姓名长度应在 {min}- - {max}")
private String name;
@Min( value = 2700, message = " " 工资不能少于 {value}")
@Max( value = 10000, message = " " 工资不能超过 {value}")
private Float salary;

在 springMVC-servlet.xml 中配置校验器:

 <!-- 配置校验器 -->
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- 校验器,使用 hibernate 校验器 -->
<property name="providerClass"
value="org.hibernate.validator.HibernateValidator"/>
</bean>

1.1.1 简单的数据校验

新建 ValidateController

 @Controller
public class ValidateController { {
@RequestMapping( "/val1.html")
public M M odelAndView validate1( @Validated Employee emp, BindingResult result) { {
if ( result. hasErrors()) { { // 如果验证错误
FieldError nameError = result. getFieldError( "name");
FieldError salaryError = result. getFieldError( "salary");
ModelAndView view = new ModelAndView();
view.setViewName( "/validate.jsp"); // 如果 有错就 返回原页面
if ( nameError != null) { {
view.addObject( "nameError", nameError.getDefaultMessage());
} }
if ( salaryError != null) { {
view.addObject( "salaryError", salaryError.getDefaultMessage());
} }
return view;
} }
// 验证成功去首页
return new ModelAndView( "/index.jsp");
} }
} }

@Validated 修饰的参数会被按照规则校验。BindingResult 会存放校验信息。在需要校验的 pojo 前边添加@Validated,在需要校验的 pojo 后边添加 BindingResultbindingResult 接收校验出错信息注意:@Validated 和 BindingResult bindingResult 是配对出现,并且形参顺序是固定的(一前一后)
页面 validate.jsp

 <form action="/val1.html" method="post">
name:<input type="text" name="name"/>${nameError}<br/>
salary:<input type="text" name="salary"/>${salaryError}<br/>
<input type="submit" value="提交"/>
</form>

运行结果:

springMVC整理05--数据校验、格式化 & 其他注解 & 数据绑定流程

提交后:

springMVC整理05--数据校验、格式化 & 其他注解 & 数据绑定流程

1.1.2  使用@ModelAttribute  和<form:>

spring 有自定义的表单标签,<form:>冒号后面的是生成 html 的标签名,如<form:input>就会生成一个<input>:

 <form:form modelAttribute="empModel" method="post" action="/val2.html">
name:<form:input path="name" /><br/>
<!--输出 name 的校验信息-->
<form:errors path="name"></form:errors><br/>
salary:<form:input path="salary" /><br/>
<form:errors path="salary"></form:errors><br/>
<input type="submit" value="Submit" /><br/>
<!--输出所有错误信息-->
<form:errors path="*"></form:errors>
</form:form>

使用这个标签需要在 jsp 页面中引入头文件:

 <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<form:>标签将的 modelAttribute 与 action 对应的方法上@ModelAttribute 修饰的对象对应。要想进入这个页面,首先要经过一个 Controller 的方法,在 Model 中添加对应的属性:

 @RequestMapping( "/goVal2.html")
public String goVal2(Model model) { {
if (! ! model. containsAttribute( "empModel")) { {
l //empModel 与页面中的 <form:form modelAttribute="empModel"> 对应
model. addAttribute( "empModel", new Employee());
} }
return "/validate.jsp";
} }

如果不经过 Controller 或者 Controller 中没有放 empModel 这个属性,那么页面就会报错:

提交表单验证的方法:

 @RequestMapping( "/val2.html")
public String test( @Validated @ModelAttribute( "empModel") Employee emp,
BindingResult result, Model model) { {
// 如果有验证错误 返回到 m form 页面
if ( result. hasErrors()) { {
// 经过 2 goVal2 方法的目的是为了设置 l empModel 属性
// 如果 l empModel 属性没有设置,页面就报错了
return goVal2( model);
} }
return "/index.jsp";
} }

1.1.3 数据校验信息国际化

国际化就是根据浏览器默认语言的不同,显示不同的提示信息:
在 Employee 中,给 id 字段添加验证信息:

 public class  Employee { {
@NotNull( message= "{NotNull.emp.id}")
private Integer

{NotNull.emp.Id}是从 properties 文件中读取属性值
在 resources 目录下创建两个文件,一个用来存放中文提示信息,一个存放英文提示信息:

注意文件的命名,xx.properties 对应的英文配置文件是 xx_en_US.properties
i18n.properties

 NotNull.emp.id=id 不能为空

i18n_en_US.properties

 NotNull.emp.id=userId can not be null

两个文件的 key 是对应的,值是不同的语言
springMVC-servlet.xml 中配置,其中 id=validator 的 bean 前面已经配置过了,这里再添加一个属性即可

 <!-- 配置校验器 -->
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- 校验器,使用 hibernate 校验器 -->
<property name="providerClass"
value="org.hibernate.validator.HibernateValidator"/>
<!--这里添加一个校验信息的数据源-->
<property name="validationMessageSource" ref="messageSource" />
</bean>
<!--自动装配校验器-->
<mvc:annotation-driven validator="validator"/>
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<!-- 在 web 环境中一定要定位到 classpath 否则默认到当前 web 应用下找 -->
<value>classpath:i18n</value>
<value>classpath:org/hibernate/validator/ValidationMessages</value>
</list>
</property>
</bean>

使用 1.1.2 节中的测试代码,运行结果:

springMVC整理05--数据校验、格式化 & 其他注解 & 数据绑定流程

将浏览器语言切换成英文,刷新页面:

springMVC整理05--数据校验、格式化 & 其他注解 & 数据绑定流程

1.2 数据格式化
1.2.1使用注解格式化

springMVC 在映射 Date 类型的属性时会报错:
如果属性是封装在实体类中的,可以使用@DateTimeFormat 注解,如 Employee 中的 hireDate属性。

 @DateTimeFormat( pattern =  "yyyy- - MM- - dd")
private Date hireDate;

12.2.2 initBinder 实现格式化
@DateTimeFormat 是在实体类中格式化日期类型的属性,所有的 Controller 中用到该实
体类都会自动使用注解定义的格式是格式化数据。除此之外,还可以使用@InitBinder 在
Controller 中自定义格式化:

 @Controller
public class DateFormatController { {
// 自定义格式化
@InitBinder
public void initBinder( ServletRequestDataBinder binder) { {
SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy- - MM- - dd");
binder.registerCustomEditor( Date. class,
new CustomDateEditor( dateFormat, true));
} }
@RequestMapping( "/date.html")
public String date( Date date) { {
System. out .println( date);
return "/format.jsp";
} }
} }

@InitBinder 定义的格式化规则对当前 Controller 有效。

1.2.3  自定义格式化

springMVC 有多种方式实现自定义数据格式化,假设现在输入一个电话号码,格式是010-12345678,我们有一个实体类,把区号和电话号码分开:

 public class  PhoneNumModel { {
private String areaCode; // 区号
private String phoneNumber; // 电话号码
//getter/setter 方法略
} }

当请求中传入一个 String 类型的参数“010-12345678”,通过 springMVC 的数据格式化,可以把 String 转换成实体类 PhoneNumModel
第一种方式:定义一个转换工具类,继承 PropertyEditorSupport

 public class  PhoneNumConverter  implements Converter< < String,  PhoneNumModel > {
// 正则表达式,定义数据规则
Pattern pattern = = Pattern.compile( "^(\\ d{3,4})- -( (\\ d{7,8})$");
@Override
public PhoneNumModel convert( String s s) { {
if ( s == null || !StringUtils.hasLength(s s)) { {
return null; // 如果没值,设值为 null
} }
Matcher matcher = pattern.matcher(s s);
if ( matcher.matches()) { {
PhoneNumModel phoneNumber = new PhoneNumModel();
phoneNumber.setAreaCode( matcher.group(1 1));
phoneNumber.setPhoneNumber( matcher.group(2 2));
return phoneNumber;
} else { {
throw new IllegalArgumentException( String.format(" " 类型转换失败,需要格
式 [010- - 12345678] ,但格式是 [%s]", s s));
} }
} }
} }

Controller 中使用@InitBinder 注册自定义转换器:

 @Controller
public class MyBinderController { {
// 自定义格式化
@InitBinder
public void initBinder( WebDataBinder binder) { {
binder.registerCustomEditor( PhoneNumModel. class, new PhoneNumEditor());
} }
@RequestMapping( "/phone.html") // 注意一定要写 @RequestParam
public ModelAndView phone( @RequestParam( "phone") PhoneNumModel phone) { {
// 绑定成功时可以看到输出
System. out .println( phone.getAreaCode());
System. out .println( phone.getPhoneNumber());
return new ModelAndView( "/format.jsp", "phone", phone);
} }
} }

测试分 url:
http://localhost:8080/phone.html?phone=010-1234567
通过@InitBinder 的方式,数据绑定规则只对当前 Controller 有效
在 Spring4.2 之后提出了一种新的转换方式,工具类实现 Converter 接口:

 public class  PhoneNumConverter  implements Converter< < String,  PhoneNumModel > {
// 正则表达式,定义数据规则
Pattern pattern = Pattern.compile( "^(\\ d{3,4})- -( (\\ d{7,8})$");
@Override
public PhoneNumModel convert( String s s) { {
if ( s == null || !StringUtils.hasLength(s s)) { {
return null; // 如果没值,设值为 null
} }
Matcher matcher = pattern.matcher(s s);
if ( matcher.matches()) { {
PhoneNumModel phoneNumber = new PhoneNumModel();
phoneNumber.setAreaCode( matcher.group(1 1));
phoneNumber.setPhoneNumber( matcher.group(2 2));
return phoneNumber;
} else { {
throw new IllegalArgumentException( String.format(" " 类型转换失败,需要格
式 [010- - 12345678] ,但格式是 [%s]", s s));
} }
} }
} }

在 springMVC 配置文件中注册自定义转换器:

 <!-- 需要将转换器设置给注解驱动 -->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
<bean id="conversionServiceFactoryBean"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="util.PhoneNumConverter"></bean>
</set>
</property>
</bean>

Controller 中不需要写@InitBinder 了。这个配置对所有 Controller 都生效。

2.  其它注解

2.1 @CookieValue

 @RequestMapping( "cookie.html")
public String cookie( @CookieValue( value = "JSESSIONID", defaultValue = "mysession")
String jsessionId) { {
System. out .println( jsessionId);
return "/index.jsp";
} }

@CookieValue 用于获取 cookie 信息。value 用于指定 cookie 的名字,defaultValue 是当对应的 cookie 为空时系统设置的默认值。required 设置为 true 表示必须。
上面的代码如果浏览器中没有 cookie,会输出 mysession。再次刷新页面,就会打印出当前的 jsesessionid,如:

springMVC整理05--数据校验、格式化 & 其他注解 & 数据绑定流程

2.2 @Value

@Value 可以实现从配置文件中读取数据并注册给 Controller 在 springMVC 配置文件种加载 properties 文件:

 <context:property-placeholder location="classpath:*.properties"/>

测试代码,这里读取的是 12.1.3 中配置文件中的 key

 // 从配置文件中读取属性
@Value( "${NotNull.emp.id}")
private String NOT_NULL_ID;
@RequestMapping( "value.html")
public String value() { {
System. out .println( NOT_NULL_ID);
return "/index.jsp";
} }

3. 数据绑定流程

springMVC整理05--数据校验、格式化 & 其他注解 & 数据绑定流程

  1. ApplicationContext 初始化时建立所有 url 和 controller 类的对应关系(用 Map 保存);
  2. 根据请求url找到对应的controller,并从controller中找到处理请求的方法
  3. Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象
  4. DataBinder 是数据绑定的核心部件,调用装配在 Spring MVC 上下文中的 ConversionService 组件进行数据类型转换、数据格式化工作。将 Servlet中的请求信息填充到入参对象中
  5. 调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果 BindingData 对象
  6. Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参。