Bean Validation в Java Enterprise
Ни для кого не секрет, что ни одна повседневная задача разработчика не обходится без потребности валидировать поступающие «извне» потоки данных: с пользовательских 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»!
Есть вопрос? Напишите в комментариях!