SpringMVC 4.1 新特性(三)集成Bean Validation 1.1(JSR-349)

来源:互联网 发布:成都气象数据 编辑:程序博客网 时间:2024/05/17 22:00

Bean Validation 1.1当前实现是Hibernate validator 5,且spring4才支持。接下来我们从以下几个方法讲解Bean Validation 1.1,当然不一定是新特性:

  • 集成Bean Validation 1.1到SpringMVC
  • 分组验证、分组顺序及级联验证
  • 消息中使用EL表达式
  • 方法参数/返回值验证
  • 自定义验证规则
  • 类级别验证器
  • 脚本验证器
  • cross-parameter,跨参数验证
  • 混合类级别验证器和跨参数验证器
  • 组合多个验证注解
  • 本地化
规范:http://beanvalidation.org/1.1/spec/ 
hibernate validator文档:http://hibernate.org/validator/




首先添加hibernate validator 5依赖:

  1. <dependency>
  2. <groupId>org.hibernate</groupId>
  3. <artifactId>hibernate-validator</artifactId>
  4. <version>5.3.4</version>
  5. </dependency>
如果想在消息中使用EL表达式,请确保EL表达式版本是 2.2或以上



  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:mvc="http://www.springframework.org/schema/mvc"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
  8. http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
  9.  
  10. <!-- 启动注解驱动的Spring MVC功能,注册请求url和注解POJO类方法的映射--><!-- <mvc:annotation-driven /> -->
  11. <mvc:annotation-driven validator="validator" />
  12.  
  13. <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
  14. <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
  15. <!--不设置则默认为classpath下的 ValidationMessages.properties -->
  16. <property name="validationMessageSource" ref="messageSource"/>
  17. </bean>
  18. <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
  19. <property name="basename" value="classpath:message/validatemessages"/>
  20. <property name="fileEncodings" value="utf-8"/>
  21. <property name="cacheSeconds" value="120"/>
  22. </bean>
  23.  
  24. </beans>

此处主要把bean validation的消息查找委托给spring的messageSource



  1. public class User implements Serializable {
  2. @NotNull(message = "{user.id.null}")
  3. private Long id;
  4.  
  5. @NotEmpty(message = "{user.name.null}")
  6. @Length(min = 5, max = 20, message = "{user.name.length.illegal}")
  7. @Pattern(regexp = "[a-zA-Z]{5,20}", message = "{user.name.illegal}")
  8. private String name;
  9.  
  10. @NotNull(message = "{user.password.null}")
  11. private String password;
  12. }

  1. user.id.null=用户编号不能为空
  2. user.name.null=用户名不能为空
  3. user.name.length.illegal=用户名长度必须在520之间
  4. user.name.illegal=用户名必须是字母
  5. user.password.null=密码不能为空

  1. @Controller
  2. @RequestMapping(value = "validate")
  3. public class ValidateController {
  4.  
  5. @RequestMapping(value="test", method = {RequestMethod.GET})
  6. @ResponseBody
  7. public Response test2(@Validated User model, BindingResult result){
  8.  
  9. if(result.hasErrors()){
  10. for(int i = 0;i<result.getFieldErrorCount();i++){
  11. FieldError error = result.getFieldErrors().get(i);
  12. System.out.println(error.getField() + "-->" + error.getDefaultMessage());
  13. }
  14. }
  15.  
  16. return new Response();
  17. }
  18.  
  19. }
其中实体User前必须加上注解@Validated@Valid,以及BindingResult必须紧随其后,不然会报错。




如果我们想在新增的情况验证idname,而修改的情况验证namepassword,怎么办? 那么就需要分组了。 

首先定义分组接口:

  1. public interface First {
  2. }
  3.  
  4. public interface Second {
  5. }

分组接口就是两个普通的接口,用于标识,类似于java.io.Serializable

接着我们使用分组接口标识实体:

  1. public class User implements Serializable {
  2.  
  3. @NotNull(message = "{user.id.null}", groups = {First.class})
  4. private Long id;
  5.  
  6. @Length(min = 5, max = 20, message = "{user.name.length.illegal}", groups = {Second.class})
  7. @Pattern(regexp = "[a-zA-Z]{5,20}", message = "{user.name.illegal}", groups = {Second.class})
  8. private String name;
  9.  
  10. @NotNull(message = "{user.password.null}", groups = {First.class, Second.class})
  11. private String password;
  12. }

验证时使用如:

  1. @Controller
  2. @RequestMapping(value = "validate")
  3. public class ValidateController {
  4.  
  5. @RequestMapping(value="test2", method = {RequestMethod.POST})
  6. @ResponseBody
  7. public Response test2(@Validated({Second.class}) User model, BindingResult result){
  8.  
  9. if(result.hasErrors()){
  10. for(int i = 0;i<result.getFieldErrorCount();i++){
  11. FieldError error = result.getFieldErrors().get(i);
  12. System.out.println(error.getField() + "-->" + error.getDefaultMessage());
  13. }
  14. }
  15. return new Response();
  16. }
  17.  
  18. }
  19.  
即通过@Validate注解标识要验证的分组;如果要验证两个的话,可以这样@Validated({First.class, Second.class})


接下来我们来看看通过分组来指定顺序;还记得之前的错误消息吗?user.name会显示两个错误消息,而且顺序不确定;如果我们先验证一个消息;如果不通过再验证另一个怎么办?可以通过@GroupSequence指定分组验证顺序:

  1. @GroupSequence({First.class, Second.class, User.class})
  2. public class User implements Serializable {
  3. private Long id;
  4.  
  5. @Length(min = 5, max = 20, message = "{user.name.length.illegal}", groups = {First.class})
  6. @Pattern(regexp = "[a-zA-Z]{5,20}", message = "{user.name.illegal}", groups = {Second.class})
  7. private String name;
  8.  
  9. private String password;
  10. }

通过@GroupSequence指定验证顺序:先验证First分组,如果有错误立即返回而不会验证Second分组,接着如果First分组验证通过了,那么才去验证Second分组,最后指定User.class表示那些没有分组的在最后。这样我们就可以实现按顺序验证分组了。

另一个比较常见的就是级联验证:

  1. public class User {
  2.  
  3. @Valid
  4. @ConvertGroup(from=First.class, to=Second.class)
  5. private Organization o;
  6.  
  7. }
级联验证只要在相应的字段上加@Valid即可,会进行级联验证;@ConvertGroup的作用是当验证o的分组是First时,那么验证o的分组是Second,即分组验证的转换。



假设我们需要显示如:用户名[NAME]长度必须在[MIN][MAX]之间,此处大家可以看到,我们不想把一些数据写死,如NAMEMINMAX;此时我们可以使用EL表达式。

  1. @Length(min = 5, max = 20, message = "{user.name.length.illegal}", groups = {First.class})
错误消息:

  1. user.name.length.illegal=用户名长度必须在{min}到{max}之间

其中我们可以使用{验证注解的属性}得到这些值;如{min}得到@Length中的min值;其他的也是类似的。

到此,我们还是无法得到出错的那个输入值,如name=zhangsan。此时就需要EL表达式的支持,首先确定引入EL jar包且版本正确。


有时候默认的规则可能还不够,有时候还需要自定义规则,比如屏蔽关键词验证是非常常见的一个功能,比如在发帖时帖子中不允许出现admin等关键词。


  1. @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
  2. @Retention(RUNTIME)
  3. //指定验证器
  4. @Constraint(validatedBy = ForbiddenValidator.class)
  5. @Documented
  6. public @interface Forbidden {
  7.  
  8. //默认错误消息
  9. String message() default "{forbidden.word}";
  10.  
  11. //分组
  12. Class<?>[] groups() default { };
  13.  
  14. //负载
  15. Class<? extends Payload>[] payload() default { };
  16.  
  17. //指定多个时使用
  18. @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
  19. @Retention(RUNTIME)
  20. @Documented
  21. @interface List {
  22. Forbidden[] value();
  23. }
  24. }

  1. public class ForbiddenValidator implements ConstraintValidator<Forbidden, String> {
  2.  
  3. private String[] forbiddenWords = {"admin"};
  4.  
  5. @Override
  6. public void initialize(Forbidden constraintAnnotation) {
  7. //初始化,得到注解数据
  8. }
  9.  
  10. @Override
  11. public boolean isValid(String value, ConstraintValidatorContext context) {
  12. if(StringUtils.isEmpty(value)) {
  13. return true;
  14. }
  15.  
  16. for(String word : forbiddenWords) {
  17. if(value.contains(word)) {
  18. return false;//验证失败
  19. }
  20. }
  21. return true;
  22. }
  23. }



阅读全文
0 0
原创粉丝点击