Знакомство с Jakarta Bean Validation 3.0

Jakarta Bean Validation — это спецификация, описывающая API для валидации объектов в рамках Jakarta EE. Вы можете использовать данную спецификацию для валидации в проектах вне зависимости от используемого фреймворка или сервера приложений. Более того, вы можете использовать Jakarta Bean Validation в полностью самостоятельных проектах, не основанных на Jakarta EE или каком-либо фреймворке.

Идейным предшественником Jakarta Bean Validation 3.0 является Bean Validation 2.0 из Java EE, однако все изменения в спецификации заключаются в переименованиях, связанных с миграцией проекта из Java EE в Jakarta EE.

В рамках этой статьи будет рассмотрен минимальный набор зависимостей, необходимый для использования Jakarta Bean Validation, описаны основные интерфейсы и ограничения или правила валидации.

Цикл статей о Jakarta Bean Validation

Данная статья является частью цикла статей о Jakarta Bean Validation:

  • Знакомство с Jakarta Bean Validation 3.0 (Вы сейчас её читаете)
  • Валидация объектов
  • Валидация методов
  • Группы валидации (В процессе написания)
  • Создание ограничения и валидатора (В процессе написания)
  • Интерполяция сообщений (В процессе написания)
  • Конфигурация валидатора (В процессе написания)

Зависимости

Основной зависимостью является библиотека jakarta.validation:jakarta.validation-api, актуальная версия которой на момент написания статьи — 3.0.2. Добавлять данную библиотеку в зависимости проекта, как правило, не нужно — если ваш проект использует Jakarta EE API, то Bean Validation уже включён в зависимости. В остальных случаях данная библиотека будет являться транзитивной зависимостью библиотек, реализующих Bean Validation (например, org.hibernate.validator:hibernate-validator), либо библиотек зависящих от реализаций (например, org.springframework.boot:spring-boot-starter-validation).

Для примеров кода в рамках данной статьи я буду использовать Hibernate Validator 8.0.1.Final:

Основные типы Jakarta Bean Validation

Основные интерфейсы, с которыми вам предстоит взаимодействовать при использовании Jakarta Bean Validation: ValidatorExecutableValidatorConstraintViolation и ConstraintValidator.

Validator

Интерфейс Validator используется для валидации объектов. Этот интерфейс объявляет следующие методы:

  • validate — для валидации объектов
  • validateProperty — для валидации отдельного свойства объекта
  • validateValue — для валидации значения относительно ограничений какого-либо свойства класса
  • getConstraintsForClass — для получения ограничений класса
  • unwrap — для получения экземпляра класса, специфичного для конкретной реализации Jakarta Bean Validation
  • forExecutables — для получения экземпляра ExecutableValidator, валидатора для методов

Создать валидатор можно одним из трёх способов:

ExecutableValidator

ExecutableValidator используется для валидации аргументов и возвращаемых значений методов и конструкторов. Он объявляет четыре метода:

  • validateParameters — для валидации аргументов метода
  • validateConstructorParameters — для валидации аргументов конструктора
  • validateReturnValue — для валидации значения, возвращённого методом
  • validateConstructorReturnValue — для валидации значения, возвращённого конструктором

Получить экземпляр ExecutableValidator можно при помощи метода Validator.forExecutables():

ConstraintViolation

Все методы валидации возвращают множество экземпляров ConstraintViolation. Этот интерфейс описывает ошибку валидации и объявляет следующие методы:

  • getMessage() — для получения интерполированного сообщения
  • getMessageTemplate() — для получения исходного шаблона сообщения
  • getRootBean() — для получения валидируемого объекта
  • getRootBeanClass() — для получения класса валидируемого объекта
  • getLeafBean() — для получения объекта, в котором возникла ошибка валидации
  • getExecutableParameters() — для получения аргументов валидируемого метода
  • getExecutableReturnValue() — для получения возвращаемого значения валидируемого метода
  • getPropertyPath() — для получения пути валидируемого свойства
  • getInvalidValue() — для получения значения, не прошедшего валидацию
  • getConstraintDescriptor() — для получения дескриптора ограничения, которое было нарушено
  • unwrap() — для получения более конкретной реализации ConstraintViolation, специфичной для провайдера

Пример получения отчёта об ошибках валидации:

ConstraintValidator

Интерфейс ConstraintValidator используется для реализации логики конкретных правил валидации и объявляет два метода:

  • initialize — для инициализации, в ходе которой можно получить доступ аннотации ограничения
  • isValid — непосредственно для реализации логики валидации.

Пример реализации ConstraintValidator для ограничения jakarta.validation.constraints.NotNull:

Ограничения или правила валидации

Ограничение (constraint) — это правило валидации, описываемое специфичной для Jakarta Bean Validation аннотацией. Ограничение может применяться использованием аннотаций в коде, либо при помощи XML.

Типичная аннотация ограничения имеет следующий вид:

Аннотация @Target задаёт список элементов, к которым аннотация ограничения может быть применена. Данный список может отличаться от продемонстрированного.

@Retention задаёт вариант доступности аннотации при компиляции исходного кода, значение RUNTIME указывает на доступность аннотации во время исполнения.

Если аннотация ограничения должна иметь возможность применения несколько раз к элементу кода, то в значении аннотации @Repeatable необходимо указать имя класса-аннотации, описывающего множественное применение. При этом описывающая множественное применение аннотация должна иметь @Target и @Retention идентичные у описываемой аннотации.

Аннотация @Documented указывает на то, что описываемая аннотация будучи применённой к элементам кода будет являться частью публичного контракта.

При помощи аннотации @Constraint можно задать список классов, валидирующих элементы кода, отмеченные описываемой аннотацией.

Аннотация ограничения должна иметь как минимум три стандартных свойства:

  • String message, содержащее шаблон сообщения, который будет использован в случае нарушения ограничения
  • Class<?>[] groups, содержащее список типов для группировки ограничений при валидации
  • Class<? extends Payload>[] payload, содержащее список типов полезной нагрузки. Данное свойство может использоваться для передачи каких-то дополнительных данных в класс-валидатор.

Кроме этих свойств класс-аннотация ограничения может содержать свойство ConstraintTarget validationAppliesTo() со значением по умолчанию ConstraintTarget.IMPLICIT, данное свойство используется для ограничений, применимых к методам и конструкторам во избежание неоднозначности объекта валидации.

Стандартные ограничения

Jakarta Bean Validation предоставляет набор стандартных ограничений, который вы можете использовать в своих проектах.

@AssertFalse

Применяется к свойствам типов java.lang.Boolean и boolean, ограничивает валидируемое значение значением false.

@AssertTrue

Аннотация, аналогичная предыдущей, применяется также к свойствам типов java.lang.Boolean и boolean, но ограничивает валидируемое значение значением true.

@DecimalMax(value=, inclusive=)

Аннотация, применяемая к свойствам типов, представляющих целые числа:

  • BigDecimal
  • BigInteger
  • CharSequence
  • byteshortintlong и их классы-обёртки

Обратите внимание на то, что float и double, а так же их классы-обёртки не поддерживаются ввиду особенностей округления.

Свойством value задаётся максимальное значение валидируемого свойства, а inclusive — включается ли максимально значение в диапазон допустимых значений.

@DecimalMin(value=, inclusive=)

Аналогичная предыдущей аннотация, также применяемая к свойствам типов, представляющих целые числа:

  • BigDecimal
  • BigInteger
  • CharSequence
  • byteshortintlong и их классы-обёртки

double и float не поддерживаются.

Свойством value задаётся минимальное значение валидируемого свойства, а inclusive — включается ли минимальное значение в диапазон допустимых значений.

@Digits(integer=, fraction=)

Аннотация, при помощи которой можно ограничить максимальное количество знаков в целой и дробной части числа.

Как и предыдущие два ограничения поддерживает типы:

  • BigDecimal
  • BigInteger
  • CharSequence
  • byteshortintlong и их классы-обёртки

Типы double и float не поддерживаются.

Свойство integer задаёт максимальное количество знаков целой части числа, fraction — дробной.

@Email

Аннотация, применяемая к свойствам типа CharSequence и его наследников (String), требующая, чтобы валидируемая строка была валидным адресом электронной почты.

@Future

Аннотация, ограничивающая значение свойства будущим временем.

Поддерживаемые типы валидируемых свойств:

  • java.util.Date
  • java.util.Calendar
  • java.time.Instant
  • java.time.LocalDate
  • java.time.LocalDateTime
  • java.time.LocalTime
  • java.time.MonthDay
  • java.time.OffsetDateTime
  • java.time.OffsetTime
  • java.time.Year
  • java.time.YearMonth
  • java.time.ZonedDateTime
  • java.time.chrono.HijrahDate
  • java.time.chrono.JapaneseDate
  • java.time.chrono.MinguoDate
  • java.time.chrono.ThaiBuddhistDate

@FutureOrPresent

Аннотация, ограничивающая значение свойства будущим или настоящим временем.

Список поддерживаемых типов идентичен таковому у @Future.

@Max(value=)

Аннотация, позволяющая указать максимальное значение свойства числового типа.

Поддерживаемые типы:

  • BigDecimal
  • BigInteger
  • CharSequence
  • byteshortintlong и их классы-обёртки

double и float не поддерживаются.

@Min(value=)

Аннотация, позволяющая указать минимальное значение свойства числового типа.

Список поддерживаемых типов идентичен таковому у @Max.

@NotBlank

Аннотация, требующая, чтобы валидиуемое строковое значение не было пустой строкой или не состояло исключительно из пробельных символов.

Применяется к CharSequence и наследникам.

@NotEmpty

Аннотация, требующая, чтобы валидируемое свойство было непустым объектом.

Поддерживаемые типы:

  • CharSequence и наследники (length())
  • Collection и наследники (size())
  • Map и наследники (size())
  • Массивы (length)

@NotNull

Аннотация, требующая, чтобы валидируемое значение было отлично от null.

@Negative

Аннотация, требующая, чтобы валидируемое значение было отрицательным числом.

Поддерживаемые типы:

  • BigDecimal
  • BigInteger
  • CharSequence
  • byteshortintlongfloatdouble и их классы-обёртки

Значение null считается валидным.

@NegativeOrZero

Аннотация, требующая, чтобы валидируемое значение было отрицательным числом, либо нулём.

Поддерживаемые типы:

  • BigDecimal
  • BigInteger
  • CharSequence
  • byteshortintlongfloatdouble и их классы-обёртки

Значение null считается валидным.

@Null

Аннотация, требующая, чтобы валидируемое значение было null.

@Past

Аннотация, ограничивающая значение свойства прошедшим временем.

Список поддерживаемых типов идентичен таковому у @Future.

@PastOrPresent

Аннотация, ограничивающая значение свойства прошедшим или настоящим временем.

Список поддерживаемых типов идентичен таковому у @Future.

@Pattern(regex=, flags=)

Аннотация, требующая, чтобы валидируемое значение типа CharSequence соответствовало регулярному выражению.

В свойстве regex указывается регулярное выражение, а в flags — массив флагов.

@Positive

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

Поддерживаемые типы:

  • BigDecimal
  • BigInteger
  • CharSequence
  • byteshortintlongfloatdouble и их классы-обёртки

Значение null считается валидным.

@PositiveOrZero

Аннотация, требующая, чтобы валидируемое значение было положительным числом, либо нулём.

Поддерживаемые типы:

  • BigDecimal
  • BigInteger
  • CharSequence
  • byteshortintlongfloatdouble и их классы-обёртки

Значение null считается валидным.

@Size(min=, max=)

Аннотация, ограничивающая размеры значения валидируемого свойства.

Поддерживаемые типы:

  • CharSequence и наследники (length())
  • Collection и наследники (size())
  • Map и наследники (size())
  • Массивы (length)

Свойством min задаётся минимальный размер (включительно), свойством max — максимальный (включительно).

В следующих статьях будет продемонстрировано применение ограничений для валидаций объектов и методов.

Полезные ссылки