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

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

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

1.1  数据校验

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

1 <!--数据校验-->
2 <dependency>
3 <groupId>org.hibernate.validator</groupId>
4 <artifactId>hibernate-validator</artifactId>
5 <version>6.0.2.Final</version>
6 </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 实体类中:

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

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

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

1.1.1 简单的数据校验

新建 ValidateController

复制代码
 1 @Controller
 2 public class  ValidateController { {
 3 @RequestMapping( "/val1.html")
 4 public M M odelAndView validate1( @Validated  Employee  emp, BindingResult  result) { {
 5 if ( result. hasErrors()) { { // 如果验证错误
 6 FieldError  nameError  =  result. getFieldError( "name");
 7 FieldError  salaryError  =  result. getFieldError( "salary");
 8 ModelAndView  view  =  new ModelAndView();
 9 view.setViewName( "/validate.jsp"); // 如果 有错就 返回原页面
10 if ( nameError  !=  null) { {
11 view.addObject( "nameError",  nameError.getDefaultMessage());
12 } }
13 if ( salaryError  !=  null) { {
14 view.addObject( "salaryError",  salaryError.getDefaultMessage());
15 } }
16 return  view;
17 } }
18 // 验证成功去首页
19 return new ModelAndView( "/index.jsp");
20 } }
21 } }
复制代码

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

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

运行结果:

提交后:

1.1.2  使用@ModelAttribute  和<form:>

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

复制代码
 1 <form:form modelAttribute="empModel" method="post" action="/val2.html">
 2 name:<form:input path="name" /><br/>
 3 <!--输出 name 的校验信息-->
 4 <form:errors path="name"></form:errors><br/>
 5 salary:<form:input path="salary" /><br/>
 6 <form:errors path="salary"></form:errors><br/>
 7 <input type="submit" value="Submit" /><br/>
 8 <!--输出所有错误信息-->
 9 <form:errors path="*"></form:errors>
10 </form:form>
复制代码

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

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

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

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

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

提交表单验证的方法:

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

1.1.3 数据校验信息国际化

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

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

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

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

1 NotNull.emp.id=id 不能为空

i18n_en_US.properties

1 NotNull.emp.id=userId can not be null

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

复制代码
 1 <!-- 配置校验器 -->
 2 <bean id="validator"
 3 class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
 4 <!-- 校验器,使用 hibernate 校验器 -->
 5 <property name="providerClass"
 6 value="org.hibernate.validator.HibernateValidator"/>
 7 <!--这里添加一个校验信息的数据源-->
 8 <property name="validationMessageSource" ref="messageSource" />
 9 </bean>
10 <!--自动装配校验器-->
11 <mvc:annotation-driven validator="validator"/>
12 <bean id="messageSource"
13 class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
14 <property name="basenames">
15 <list>
16 <!-- 在 web 环境中一定要定位到 classpath 否则默认到当前 web 应用下找 -->
17 <value>classpath:i18n</value>
18 <value>classpath:org/hibernate/validator/ValidationMessages</value>
19 </list>
20 </property>
21 </bean>
复制代码

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

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

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

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

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

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

复制代码
 1 @Controller
 2 public class  DateFormatController { {
 3 // 自定义格式化
 4 @InitBinder
 5 public void initBinder( ServletRequestDataBinder  binder) { {
 6 SimpleDateFormat  dateFormat  =  new SimpleDateFormat( "yyyy- - MM- - dd");
 7 binder.registerCustomEditor( Date. class,
 8 new CustomDateEditor( dateFormat,  true));
 9 } }
10 @RequestMapping( "/date.html")
11 public  String date( Date  date) { {
12 System. out .println( date);
13 return  "/format.jsp";
14 } }
15 } }
复制代码

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

1.2.3  自定义格式化

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

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

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

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

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

复制代码
 1 @Controller
 2 public class  MyBinderController { {
 3 // 自定义格式化
 4 @InitBinder
 5 public void initBinder( WebDataBinder  binder) { {
 6 binder.registerCustomEditor( PhoneNumModel. class,  new PhoneNumEditor());
 7 } }
 8 @RequestMapping( "/phone.html") // 注意一定要写 @RequestParam
 9 public  ModelAndView phone( @RequestParam( "phone")  PhoneNumModel  phone) { {
10 // 绑定成功时可以看到输出
11 System. out .println( phone.getAreaCode());
12 System. out .println( phone.getPhoneNumber());
13 return new ModelAndView( "/format.jsp",  "phone",  phone);
14 } }
15 } }
复制代码

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

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

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

复制代码
 1 <!-- 需要将转换器设置给注解驱动 -->
 2 <mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
 3 <bean id="conversionServiceFactoryBean"
 4 class="org.springframework.context.support.ConversionServiceFactoryBean">
 5 <property name="converters">
 6 <set>
 7 <bean class="util.PhoneNumConverter"></bean>
 8 </set>
 9 </property>
10 </bean>
复制代码

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

 

2.  其它注解

2.1 @CookieValue

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

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

2.2 @Value

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

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

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

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

3. 数据绑定流程

  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 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参。

 

posted @   少说点话  阅读(693)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
网站运行:7年51天17时23分55秒
点击右上角即可分享
微信分享提示

目录导航