Введение в Apache BVal

Введение в Apache BVal

1. Вступление

В этой статье мы рассмотримApache BVal library’s implementation of the Java Bean Validation specification (JSR 349).

2. Maven Зависимости

Чтобы использоватьApache BVal, нам сначала нужно добавить следующие зависимости в наш файлpom.xml:


    org.apache.bval
    bval-jsr
    1.1.2


    javax.validation
    validation-api
    1.1.0.Final

Пользовательские ограниченияBVal можно найти в необязательной зависимостиbval-extras:


    org.apache.bval
    bval-extras
    1.1.2

Последние версииbval-jsr,bval-extras иvalidation-api можно загрузить с Maven Central.

3. Применение ограничений

Apache BVal предоставляет реализации для всех ограничений, определенных в пакетеjavax.validation. Чтобы применить ограничение к свойству bean-компонента, мы можемadd the constraint annotation to the property declaration.

Давайте создадим классUser с четырьмя атрибутами, а затем применим аннотации@NotNull,@Size и@Min:

public class User {

    @NotNull
    private String email;

    private String password;

    @Size(min=1, max=20)
    private String name;

    @Min(18)
    private int age;

    // standard constructor, getters, setters
}

4. Проверка бобов

Чтобы проверить ограничения, примененные к классуUser, нам нужно получить экземплярValidatorFactory и один или несколько экземпляровValidator.

4.1. Получение aValidatorFactory

В документацииApache BVal рекомендуется получить единственный экземпляр этого класса, так как создание фабрики - сложный процесс:

ValidatorFactory validatorFactory
  = Validation.byProvider(ApacheValidationProvider.class)
  .configure().buildValidatorFactory();

4.2. Получение aValidator

Затем нам нужно получить экземплярValidator изvalidatorFactory, определенного выше:

Validator validator = validatorFactory.getValidator();

This is a thread-safe implementation, поэтому мы можем безопасно повторно использовать уже созданные экземпляры.

КлассValidator предлагает три метода определения допустимости bean-компонента:validate(),validateProperty() иvalidateValue().

Каждый из этих методов возвращает набор объектовConstraintViolation, содержащих информацию об ограничении, которое не было соблюдено.

4.3. validate() API

Методvalidate() проверяет достоверность всего bean-компонента, что означает, что в качестве параметра передается именноverifies all the constraints applied to properties of an object.

Давайте создадим тестJUnit, в котором мы определяем объектUser и используем методvalidate() для проверки его свойств:

@Test
public void givenUser_whenValidate_thenValidationViolations() {
    User user
      = new User("[email protected]", "pass", "nameTooLong_______________", 15);

    Set> violations = validator.validate(user);
    assertTrue("no violations", violations.size() > 0);
}

4.4. validateProperty() API

МетодvalidateProperty() можно использовать дляvalidate a single property of a bean.

Давайте создадим тестJUnit, в котором мы определим объектUser со свойствомage меньше требуемого минимального значения 18, и проверим, что проверка этого свойства приводит к одному нарушению:

@Test
public void givenInvalidAge_whenValidateProperty_thenConstraintViolation() {
    User user = new User("[email protected]", "pass", "Ana", 12);

    Set> propertyViolations
      = validator.validateProperty(user, "age");

    assertEquals("size is not 1", 1, propertyViolations.size());
}

4.5. validateValue() API

МетодvalidateValue() можно использовать дляcheck if some value would be a valid value for a property of a bean перед его установкой для bean-компонента.

Давайте создадим тестJUnit с объектомUser, а затем проверим, что значение20 является допустимым значением для свойстваage:

@Test
public void givenValidAge_whenValidateValue_thenNoConstraintViolation() {
    User user = new User("[email protected]", "pass", "Ana", 18);

    Set> valueViolations
      = validator.validateValue(User.class, "age", 20);

    assertEquals("size is not 0", 0, valueViolations.size());
}

4.6. ЗакрытиеValidatorFactory

After using the ValidatorFactory, we must remember to close it в конце:

if (validatorFactory != null) {
    validatorFactory.close();
}

5. Не -JSR Ограничения

БиблиотекаApache BVal также предоставляетseries of constraints that are not a part of the JSR specification и предоставляет дополнительные и более мощные возможности проверки.

Пакетbval-jsr содержит два дополнительных ограничения:@Email для проверки действительного адреса электронной почты и@NotEmpty для обеспечения того, чтобы значение не было пустым.

Остальные пользовательские ограниченияBVal предоставляются в дополнительном пакетеbval-extras.

Этот пакет содержитconstraints for verifying various number formats, например аннотацию@IBAN, которая гарантирует, что номер является действительным международным номером банковского счета, аннотацию@Isbn, которая проверяет действительный стандартный номер книги, и@EAN13 аннотация для проверки международного артикульного номера.

Библиотека также предоставляетannotations for ensuring the validity of various types of credit card numbers:@AmericanExpress,@Diners,@Discover,@Mastercard и@Visa.

Вы можетеdetermine if a value contains a valid domain or Internet Address, используя аннотации@Domain и@InetAddress.

И, наконец, пакет содержит аннотации@Directory и@NotDirectory дляverifying whether a File object is a directory or not.

Давайте определим дополнительные свойства в нашем классеUser и применим к ним некоторые из аннотаций, отличных отJSR:

public class User {

    @NotNull
    @Email
    private String email;

    @NotEmpty
    private String password;

    @Size(min=1, max=20)
    private String name;

    @Min(18)
    private int age;

    @Visa
    private String cardNumber = "";

    @IBAN
    private String iban = "";

    @InetAddress
    private String website = "";

    @Directory
    private File mainDirectory = new File(".");

    // standard constructor, getters, setters
}

Ограничения можно тестировать аналогично ограничениямJSR:

@Test
public void whenValidateNonJSR_thenCorrect() {
    User user = new User("[email protected]", "pass", "Ana", 20);
    user.setCardNumber("1234");
    user.setIban("1234");
    user.setWebsite("10.0.2.50");
    user.setMainDirectory(new File("."));

    Set> violations
      = validator.validateProperty(user,"iban");

    assertEquals("size is not 1", 1, violations.size());

    violations = validator.validateProperty(user,"website");

    assertEquals("size is not 0", 0, violations.size());

    violations = validator.validateProperty(user, "mainDirectory");

    assertEquals("size is not 0", 0, violations.size());
}

Хотя эти дополнительные аннотации удобны для потенциальных потребностей в валидации, одним из недостатков использования аннотаций, которые не являются частью спецификацииJSR, является то, что вы не можете легко переключиться на другую реализациюJSR, если это станет необходимо позже. .

6. Пользовательские ограничения

Чтобы определить наши собственные ограничения, нам сначала нужно создать аннотацию в соответствии со стандартным синтаксисом.

Давайте создадим аннотациюPassword, которая будет определять условия, которым должен удовлетворять пароль пользователя:

@Constraint(validatedBy = { PasswordValidator.class })
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface Password {
    String message() default "Invalid password";

    Class[] groups() default {};

    Class[] payload() default {};

    int length() default 6;

    int nonAlpha() default 1;
}

The actual validation of the password value is done in a class that implements the ConstraintValidator interface - в нашем случае классPasswordValidator. Этот класс переопределяет методisValid() и проверяет, меньше ли длинаpassword атрибутаlength, и содержит ли он меньше указанного количества не буквенно-цифровых символов вnonAlphaатрибут s:

public class PasswordValidator
  implements ConstraintValidator {

    private int length;
    private int nonAlpha;

    @Override
    public void initialize(Password password) {
        this.length = password.length();
        this.nonAlpha = password.nonAlpha();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext ctx) {
        if (value.length() < length) {
            return false;
        }
        int nonAlphaNr = 0;
        for (int i = 0; i < value.length(); i++) {
            if (!Character.isLetterOrDigit(value.charAt(i))) {
                nonAlphaNr++;
            }
        }
        if (nonAlphaNr < nonAlpha) {
            return false;
        }
        return true;
    }
}

Давайте применим наше собственное ограничение к свойствуpassword классаUser:

@Password(length = 8)
private String password;

Мы можем создать тестJUnit, чтобы убедиться, что недопустимое значениеpassword приводит к нарушению ограничения:

@Test
public void givenValidPassword_whenValidatePassword_thenNoConstraintViolation() {
    User user = new User("[email protected]", "password", "Ana", 20);
    Set> violations
      = validator.validateProperty(user, "password");

    assertEquals(
      "message incorrect",
      "Invalid password",
      violations.iterator().next().getMessage());
}

Теперь давайте создадим тестJUnit, в котором мы проверяем допустимое значениеpassword:

@Test
public void givenValidPassword_whenValidatePassword_thenNoConstraintViolation() {
    User user = new User("[email protected]", "password#", "Ana", 20);

    Set> violations
      = validator.validateProperty(user, "password");
    assertEquals("size is not 0", 0, violations.size());
}

7. Заключение

В этой статье мы продемонстрировали использование реализации проверки bean-компонентаApache BVal.

Полный исходный код этой статьи можно найти наover on GitHub.