Contents

Hibernate中ParameterMessageInterpolator简介

1. 概述

Java JSR 380 的特性之一是在使用参数插入验证消息时允许表达式。

当我们使用 Hibernate Validator 时,**我们需要将 Java JSR 341 的统一实现之一作为依赖添加到我们的项目中。**JSR 341 也称为表达式语言 API。

但是,如果我们不需要根据我们的用例支持解析表达式,那么添加一个额外的库可能会很麻烦。

在这个简短的教程中,我们将了解如何在 Hibernate Validator中配置ParameterMessageInterpolator

2. MessageInterpolator

除了验证 Java bean 的基础知识 之外,Bean Validation API 的MessageInterpolator是一种抽象,它为我们提供了一种执行简单插值的方法,而无需解析表达式。

此外,*Hibernate Validator 提供了一个基于非表达式的*ParameterMessageInterpolator,因此我们不需要任何额外的库来配置它。

3. 设置自定义消息插值器

要移除表达式语言依赖,我们可以使用自定义消息插值器并在不支持表达式的情况下配置 Hibernate Validator。 让我们展示一些设置自定义消息插值器的便捷方法。在我们的案例中,我们将使用内置的ParameterMessageInterpolator

3.1. 配置ValidatorFactory

设置自定义消息插值器的一种方法是在引导时配置ValidatorFactory

因此,我们可以使用ParameterMessageInterpolator构建一个ValidatorFactory实例:

ValidatorFactory validatorFactory = Validation.byDefaultProvider()
  .configure()
  .messageInterpolator(new ParameterMessageInterpolator())
  .buildValidatorFactory();

3.2. 配置Validator

同样,我们可以在初始化Validator实例时设置ParameterMessageInterpolator

Validator validator = validatorFactory.usingContext()
  .messageInterpolator(new ParameterMessageInterpolator())
  .getValidator();

4. 执行验证

要了解ParameterMessageInterpolator是如何工作的,我们需要一个带有一些 JSR 380 注释的示例 Java bean。

4.1. 示例 Java Bean

让我们定义我们的示例 Java bean Person

public class Person {
    @Size(min = 10, max = 100, message = "Name should be between {min} and {max} characters")
    private String name;
    @Min(value = 18, message = "Age should not be less than {value}")
    private int age;
    @Email(message = "Email address should be in a correct format: ${validatedValue}")
    private String email;
    // standard getters and setters
}

4.2. 测试消息参数

当然,要执行我们的验证,我们应该使用从 ValidatorFactory 访问的Validator实例,我们之前已经配置了该实例。

所以,我们需要访问我们的Validator

Validator validator = validatorFactory.getValidator();

之后,我们可以为name字段编写测试方法:

@Test
public void givenNameLengthLessThanMin_whenValidate_thenValidationFails() {
    Person person = new Person();
    person.setName("John Doe");
    person.setAge(18);
    Set<ConstraintViolation<Person>> violations = validator.validate(person);
 
    assertEquals(1, violations.size());
    ConstraintViolation<Person> violation = violations.iterator().next();
 
    assertEquals("Name should be between 10 and 100 characters", violation.getMessage());
}

验证消息正确插入了*{min}{max}*变量:

Name should be between 10 and 100 characters

接下来,让我们为age字段编写一个类似的测试:

@Test
public void givenAgeIsLessThanMin_whenValidate_thenValidationFails() {
    Person person = new Person();
    person.setName("John Stephaner Doe");
    person.setAge(16);
    Set<ConstraintViolation<Person>> violations = validator.validate(person);
 
    assertEquals(1, violations.size());
    ConstraintViolation<Person> violation = violations.iterator().next();
 
    assertEquals("Age should not be less than 18", violation.getMessage());
}

同样,验证消息按照我们的预期使用变量*{value}*正确插入:

Age should not be less than 18

4.3. 测试表达式

要查看ParameterMessageInterpolator如何处理表达式,让我们为email字段编写另一个包含简单*${validatedValue}*表达式的测试:

@Test
public void givenEmailIsMalformed_whenValidate_thenValidationFails() {
    Person person = new Person();
    person.setName("John Stephaner Doe");
    person.setAge(18);
    person.setEmail("johndoe.dev");
    
    Set<ConstraintViolation<Person>> violations = validator.validate(person);
 
    assertEquals(1, violations.size());
    
    ConstraintViolation<Person> violation = violations.iterator().next();
 
    assertEquals("Email address should be in a correct format: ${validatedValue}", violation.getMessage());
}

这一次,表达式*${validatedValue}*没有被插值。

*ParameterMessageInterpolator仅支持参数的插值,不解析使用$*表示法的表达式。**相反,它只是简单地返回它们未插值。