目前Bean Validation(JSR-349)的新特性可以到官网查看,之前用的参数校验有很多,主要集中于:

  • 跨参数验证(比如密码和确认密码的验证)
  • 支持在消息中使用EL表达式
  • 方法参数/返回值验证
  • CDI和依赖注入
  • 分组转换

最开始接触Java Bean Validation的时候是Hibernate validator 5,而且spring4才开始使用的,接下来我们从以下几个方法讲解Bean Validation,当然不一定是新特性:

  • 集成Java Bean Validation到SpringMVC

    • 分组验证、分组顺序及级联验证
    • 消息中使用EL表达式
    • 方法参数/返回值验证
    • 自定义验证规则
    • 类级别验证器
    • 脚本验证器
    • cross-parameter,跨参数验证
    • 混合类级别验证器和跨参数验证器
    • 组合多个验证注解
    • 本地化

因为大多数时候验证都配合web框架使用,而且很多朋友都咨询过如分组/跨参数验证,所以本文介绍下这些,且是和SpringMVC框架集成的例子,其他使用方式(比如集成到JPA中)可以参考其官方文档:

添加hibernate validator依赖:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>latest.version</version>
</dependency>

如果想在消息中使用EL表达式,请确保EL表达式版本是 2.2或以上,至少需要进行Tomcat7以上。

<dependency>  
    <groupId>javax.el</groupId>  
    <artifactId>javax.el-api</artifactId>  
    <version>2.2.4</version>  
    <scope>provided</scope>  
</dependency>  

请确保您使用的Web容器有相应版本的el jar包。

web服务的展示

@Controller
public class HelloWorldController {
    @RequestMapping("/validate/hello")
    public String validate(@Valided @ModelAttribute("user") UserModel user, Errors errors) {
        if(errors.hasErrors()) {
            return "validate/error";
        }
        return "redirect:/success";
    }
}

硬编码错误消息

直接在验证约束注解上指定错误消息,如下所示:

@NotNull(message = "用户名不能为空")
@Length(min=5, max=20, message="用户名长度必须在5-20之间")
@Pattern(regexp = "^[a-zA-Z_]\\w{4,19}$", message = "用户名必须以字母下划线开头,可由字母数字下划线组成")
private String username;

如上所示,错误消息使用硬编码指定,这种方式是不推荐使用的,因为在如下场景是不适用的:

  1. 在国际化场景下,需要对不同的国家显示不同的错误消息;
  2. 需要更换错误消息时是比较麻烦的,需要找到相应的类进行更换,并重新编译发布。

交接于资源文件

默认的错误消息文件是/org/hibernate/validator/ValidationMessages.properties,如下图所示:
resource

public class User implements Serializable {
    @NotNull(message = "{user.id.null}")
    private Long id;
    @NotEmpty(message = "{user.name.null}")
    @Length(min = 5, max = 20, message = "{user.name.length.illegal}")
    @Pattern(regexp = "[a-zA-Z]{5,20}", message = "{user.name.illegal}")
    private String name;
    @NotNull(message = "{user.password.null}")
    private String password;
}

错误信息内容

默认的错误消息键值如下图所示:
【Spring功能介绍】SpringMVC集成Java Bean Validation实现参数检验功

  • 消息键默认为:验证约束注解的全限定类名.message。

  • 在我们之前的测试文件中,错误消息键值是使用默认的,如何自定义错误消息文件和错误消息键值呢?

自定义的错误消息文件和错误消息键值

  • 自定义错误消息文件里的错误消息键值将覆盖默认的错误消息文件中的错误消息键值。
  • 自定义错误消息文件是具有国际化功能的。

定义错误消息文件

在类装载路径的根下创建ValidationMessages.properties文件,如在src目录下创建会自动复制到类装载路径的根下,并添加如下消息键值

自定义的错误消息文件和错误消息键值
自定义的错误消息文件里的错误消息键值将覆盖默认的错误消息文件中的错误消息键值。我们自定义的错误消息文件是具有国际化功能的。

javax.validation.constraints.Pattern.message=用户名必须以字母或下划线开头,后边可以跟字母数字下划线,长度在5-20之间。

  • org.springframework.validation.beanvalidation.LocalValidatorFactoryBean

  • org.hibernate.validator.HibernateValidator

此时错误消息键值的查找会先到classpath下ValidationMessages.properties中找,找不到再到默认的错误消息文件中找。

使用Spring的MessageSource Bean进行消息键值的查找
如果我们的环境是与spring集成,还是应该使用Spring提供的消息支持,具体配置如下

  • org.springframework.context.support.ReloadableResourceBundleMessageSource
    • basename:classpath:messages
    • fileEncodings:utf-8
    • cacheSeconds:120

在消息文件src/messages.properties中添加如下错误消息:

  • javax.validation.constraints.Pattern.message=用户名必须以字母或下划线开头,后边可以跟字母数字下划线,长度在5-20之间。

配置了messageSource Bean时,默认将为验证的对象自动生成如下错误消息键:

  • 验证错误注解简单类名.验证对象名.字段名
  • 验证错误注解简单类名.字段名
  • 验证错误注解简单类名.字段类型全限定类名
  • 验证错误注解简单类名

使用的优先级是:从高到低,即最前边的具有最高的优先级,而且以上所有默认的错误消息键优先级高于自定义的错误消息键。

public String pattern(@Valided @ModelAttribute("model") PatternModel model, Errors errors)

将自动产生如下错误消息键:

  • Pattern.model.value=验证错误注解简单类名.验证对象名.字段名
  • Pattern.value=验证错误注解简单类名.字段名
  • Pattern.java.lang.String=验证错误注解简单类名.字段类型全限定类名
  • Pattern=验证错误注解简单类名

自定义错误消息键值

大部分场景下,以上两种方式无法满足我们的需求,因此我们需要自定义错误消息键值。

public class PatternModel {
    @Pattern(regexp = "^[a-zA-Z_][\\w]{4,19}$", message="{user.name.error}")
    private String value;
}

在消息文件src/messages.properties中添加如下错误消息:

user.name.error=用户名格式不合法

在消息文件src/messages.properties中添加如下错误消息:

@Length(min=5, max=20, message="{user.name.length.error}")  
  • user.name.length.error=用户名长度必须在5-20之间

错误消息中的5-20应该是从@Length验证约束注解中获取的,而不是在错误消息中硬编码,因此我们需要占位符的支持:

@Length(min=5, max=20, message="{user.name.length.error}")

错误消息可以这样写:用户名长度必须在{min}-{max}之间。

错误消息占位符规则:{验证注解属性名}:

  • @Length有min和max属性,则在错误消息文件中可以通过{min}和{max}来获取;

  • @Max有value属性,则在错误消息文件中可以通过{value}来获取。
user.name.length.error=用户名长度必须在{min}-{max}之间

功能处理方法上多个验证参数的处理

当我们在一个功能处理方法上需要验证多个模型对象时,需要通过如下形式来获取验证结果:

@RequestMapping("/validate/multi")  
public String multi(  
            @Valid @ModelAttribute("a") A a, BindingResult aErrors,  
            @Valid @ModelAttribute("b") B b, BindingResult bErrors) {  
        if(aErrors.hasErrors()) { //如果a模型对象验证失败  
            return "validate/error";  
        }  
        if(bErrors.hasErrors()) { //如果a模型对象验证失败  
            return "validate/error";  
        }  
        return "redirect:/success";  
}

每一个模型对象后边都需要跟一个Errors或BindingResult对象来保存验证结果,其方法体内部可以使用这两个验证结果对象来选择出错时跳转的页面。