Несколько дней новогоднего волшебства:
Успейте начать обучение в 2018-ом году со скидкой до 30%!
Выбрать курс

Bean Validation в Java Enterprise

JavaEE_Deep_29-5020-f54de6.08_site.png

Ни для кого не секрет, что ни одна повседневная задача разработчика не обходится без потребности валидировать поступающие «извне» потоки данных: с пользовательских HTML-форм, внешних систем и т.д. На первый взгляд, ничего нет плохого в том, что каждый программист пытается по-своему решить эту задачу (фактически написав «очередной велосипед», используя множество проверок, состоящих из if-else блоков).

Но при таком подходе высока вероятность, что в будущем поддержка такого «спагетти»-кода станет крайне затруднительной из-за постоянной потребности в модификации атрибутов класса или в случае переиспользования проверок в различных участках системы, например, на клиенте и на сервере. Чтобы избежать подобных проблем, в стеке JavaEE предусмотрели спецификацию Bean Validation 2.0 (JSR 380).

Итак, что же это за зверь?

Основная идея данной спецификации заключается в использовании таких аннотаций, как @Null /@NotNull, @Size, @Past /@Future, @Min /@Max и т. д., размещаемых над полями класса для проверки заранее ожидаемых значений, вхождения в границы диапазона, соответствия регулярным выражениям и т. д.

Более того, эту функциональность без каких-либо проблем можно использовать и в JavaSE-приложениях, для чего лишь потребуется в pom.xml подключить следующие зависимости:

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>${validation.version}</version>
</dependency>
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>${validator.version}</version>
</dependency>
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator-annotation-processor</artifactId>
    <version>${validator.version}</version>
</dependency>

Валидатор Hibernate (на момент написания заметки самая актуальная версия validator.version — 6.0.11.Final, validation.version — 2.0.1.Final) является референсной имплементацией спецификации Bean Validation.

Обращаю внимание, что добавление этой библиотеки в проект не добавит поддержки аспектов персистентности одноимённой библиотеки.

Немного практики

Теперь посмотрим, как же использовать всю мощь спецификации в прикладном коде. Представим, что определён класс User с некоторым набором характеристик:

import javax.validation.constraints.*;

public class User {

    @NotBlank(message = "Name cannot be null")
    private String name;

    @AssertTrue
    private boolean hasWork;

    @Size(min = 10, max = 500, message
            = "Description should be in range of 10 and 500 symbols")
    private String description;

    @Min(value = 18, message = "Age should be greater than 18")
    @Max(value = 100, message = "Age should be less than 100")
    private int age;

    @NotNull
    @Email(message = "Email must be valid")
    private String email;

    // setters and getters
}

Уже только взглянув на сами аннотации, интуитивно догадываемся, что свойства: - name должно быть отличным от null и пустой строки “”; - hasWork иметь значение истинно; - description — строка, количество символов которой не должно превышать 500 и быть меньше 10 символов; - age лежит в диапазоне 18..100, включительно; - email должно удовлетворять формату электронных писем.

В примере представлены далеко не все возможные аннотации, определяющие ограничения значений полей бинов. С полным списком можно познакомиться в пакете javax.validation.constraints.

Также акцентирую внимание на том, что допустимо комбинирование разных ограничений, не противоречащих здравой логике (в приведенном примере — одновременно использовать @Min @Max и @NotNull @Email).

Не менее интересной является возможность использования данных аннотаций для элементов коллекций в следующем виде:

Set<@NotBlank String> hobbies;

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

User user = new User();
user.setHasWork(true);
user.setDescription("Simple description about author!");
user.setAge(30);

Осталось научиться проверять бины и получать информацию о потенциальных проблемах валидации такого рода объектов. Для этого важно получить ссылку на объект класса Validator. В JavaEE-контейнере можно просто заинжектить данный объект, используя спецификацию CDI:

@Inject
Validator validator;

Для JavaSE-приложений можно инициализировать данный объект явно:

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();

У объекта validator есть несколько полезных методов validate, проверяющих все свойства бина — validateProperty и validateValue, испытывающие конкретное свойство объекта.

Set<ConstraintViolation<User>> violations = validator.validate(user);
for (ConstraintViolation<User> violation : violations) {
    log.error(violation.getMessage());
}

В текущем примере поля name и email не проинициализированы и об этом будет сообщено в логе. Для получения невалидного значения достаточно вызвать:

violation.getInvalidValue()

Вот и познакомились с базовыми возможностями спецификации Bean Validation!

Если у вас появилось желание узнать подробности и узнать, например, как создавать свои кастомные валидаторы, каким образом объединять проверки в группы, как использовать эти возможности с другими спецификациями (JAX-RS, JPA), а также делать сообщения интернациональными — приходите к нам на курс «Разработчик Java Enterprise»!

Есть вопрос? Напишите в комментариях!

Автор
0 комментариев
Для комментирования необходимо авторизоваться