Перейти к основному содержимому

Введение в Apache BVal

· 7 мин. чтения

1. Введение

В этой статье мы рассмотрим реализацию библиотеки Apache BVal спецификации Java Bean Validation ( JSR 349 ) .

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

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

<dependency>
<groupId>org.apache.bval</groupId>
<artifactId>bval-jsr</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>

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

<dependency>
<groupId>org.apache.bval</groupId>
<artifactId>bval-extras</artifactId>
<version>1.1.2</version>
</dependency>

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

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

Apache BVal предоставляет реализации для всех ограничений, определенных в пакете javax.validation . Чтобы применить ограничение к свойству компонента, мы можем добавить аннотацию ограничения к объявлению свойства .

Давайте создадим класс 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. Получение ValidatorFactory

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

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

4.2. Получение валидатора

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

Validator validator = validatorFactory.getValidator();

Это потокобезопасная реализация , поэтому мы можем безопасно повторно использовать уже созданные экземпляры.

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

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

4.3. API проверки()

Метод validate() проверяет действительность всего bean-компонента, что означает, что он проверяет все ограничения, применяемые к свойствам объекта , который передается в качестве параметра.

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

@Test
public void givenUser_whenValidate_thenValidationViolations() {
User user
= new User("ana@yahoo.com", "pass", "nameTooLong_______________", 15);

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

4.4. API проверки свойства ()

Метод validateProperty() можно использовать для проверки одного свойства компонента .

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

@Test
public void givenInvalidAge_whenValidateProperty_thenConstraintViolation() {
User user = new User("ana@yahoo.com", "pass", "Ana", 12);

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

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

4.5. API проверки значения ()

Метод validateValue() можно использовать для проверки того, будет ли какое-либо значение допустимым значением свойства компонента, прежде чем устанавливать его в компоненте.

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

@Test
public void givenValidAge_whenValidateValue_thenNoConstraintViolation() {
User user = new User("ana@yahoo.com", "pass", "Ana", 18);

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

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

4.6. Закрытие ValidatorFactory

После использования ValidatorFactory мы должны не забыть закрыть его в конце:

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

5. Ограничения , не относящиеся к JSR

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

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

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

Этот пакет содержит ограничения для проверки различных числовых форматов , таких как аннотация @IBAN , которая гарантирует, что номер является допустимым международным номером банковского счета, аннотация @Isbn , которая проверяет действительный стандартный номер книги, и аннотация @EAN13 для проверки международного номера статьи. .

Библиотека также предоставляет аннотации для проверки правильности различных типов номеров кредитных карт : @AmericanExpress , @Diners , @Discover , @Mastercard и @Visa .

Вы можете определить, содержит ли значение действительный домен или интернет-адрес , используя аннотации @Domain и @InetAddress .

И, наконец, пакет содержит аннотации @Directory и @NotDirectory для проверки того, является ли объект File каталогом или нет .

Давайте определим дополнительные свойства в нашем классе 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("ana@yahoo.com", "pass", "Ana", 20);
user.setCardNumber("1234");
user.setIban("1234");
user.setWebsite("10.0.2.50");
user.setMainDirectory(new File("."));

Set<ConstraintViolation<User>> 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<? extends Payload>[] payload() default {};

int length() default 6;

int nonAlpha() default 1;
}

Фактическая проверка значения пароля выполняется в классе, реализующем интерфейс ConstraintValidator — в нашем случае это класс PasswordValidator . Этот класс переопределяет метод isValid() и проверяет, меньше ли длина пароля , чем атрибут length , и содержит ли он меньше, чем указанное количество небуквенно-цифровых символов в атрибуте nonAlpha :

public class PasswordValidator 
implements ConstraintValidator<Password, String> {

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;
}
}

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

@Password(length = 8)
private String password;

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

@Test
public void givenValidPassword_whenValidatePassword_thenNoConstraintViolation() {
User user = new User("ana@yahoo.com", "password", "Ana", 20);
Set<ConstraintViolation<User>> violations
= validator.validateProperty(user, "password");

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

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

@Test
public void givenValidPassword_whenValidatePassword_thenNoConstraintViolation() {
User user = new User("ana@yahoo.com", "password#", "Ana", 20);

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

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

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

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