1. Обзор
Версия 2.0 спецификации Java Bean Validation
добавляет несколько новых функций, среди которых возможность проверки элементов контейнеров.
Эта новая функциональность использует аннотации типов, представленные в Java 8. Поэтому для работы требуется Java версии 8 или выше.
Аннотации проверки можно добавлять в такие контейнеры, как коллекции, необязательные
объекты и другие встроенные, а также настраиваемые контейнеры.
Для ознакомления с проверкой Java Bean Validation
и тем, как настроить необходимые нам зависимости Maven , ознакомьтесь с нашей
предыдущей статьей здесь .
В следующих разделах мы сосредоточимся на проверке элементов каждого типа контейнера.
2. Элементы коллекции
Мы можем добавить аннотации проверки к элементам коллекций типа java.util.Iterable
, java.util.List
и java.util.Map
.
Давайте посмотрим на пример проверки элементов списка:
public class Customer {
List<@NotBlank(message="Address must not be blank") String> addresses;
// standard getters, setters
}
В приведенном выше примере мы определили свойство address для класса
Customer, которое содержит элементы, которые не могут быть пустыми
строками
.
Обратите внимание, что проверка @NotBlank
применяется к элементам String
, а не ко всей коллекции . Если коллекция пуста, проверка не применяется.
Давайте проверим, что если мы попытаемся добавить пустую строку
в список адресов
, среда проверки вернет ConstraintViolation
:
@Test
public void whenEmptyAddress_thenValidationFails() {
Customer customer = new Customer();
customer.setName("John");
customer.setAddresses(Collections.singletonList(" "));
Set<ConstraintViolation<Customer>> violations =
validator.validate(customer);
assertEquals(1, violations.size());
assertEquals("Address must not be blank",
violations.iterator().next().getMessage());
}
Далее давайте посмотрим, как мы можем проверить элементы коллекции типа Map
:
public class CustomerMap {
private Map<@Email String, @NotNull Customer> customers;
// standard getters, setters
}
Обратите внимание, что мы можем добавить аннотации проверки как для ключа, так и для значения элемента Map
.
Давайте проверим, что добавление записи с недопустимым адресом электронной почты приведет к ошибке проверки:
@Test
public void whenInvalidEmail_thenValidationFails() {
CustomerMap map = new CustomerMap();
map.setCustomers(Collections.singletonMap("john", new Customer()));
Set<ConstraintViolation<CustomerMap>> violations
= validator.validate(map);
assertEquals(1, violations.size());
assertEquals(
"Must be a valid email",
violations.iterator().next().getMessage());
}
3. Дополнительные
значения
Ограничения проверки также могут быть применены к необязательному
значению:
private Integer age;
public Optional<@Min(18) Integer> getAge() {
return Optional.ofNullable(age);
}
Давайте создадим клиента
со слишком низким возрастом и убедимся, что это приводит к ошибке проверки:
@Test
public void whenAgeTooLow_thenValidationFails() {
Customer customer = new Customer();
customer.setName("John");
customer.setAge(15);
Set<ConstraintViolation<Customer>> violations
= validator.validate(customer);
assertEquals(1, violations.size());
}
С другой стороны, если возраст
равен нулю, то необязательное
значение не проверяется:
@Test
public void whenAgeNull_thenValidationSucceeds() {
Customer customer = new Customer();
customer.setName("John");
Set<ConstraintViolation<Customer>> violations
= validator.validate(customer);
assertEquals(0, violations.size());
}
4. Неуниверсальные элементы-контейнеры
Помимо добавления аннотаций для аргументов типа, мы также можем применять проверку к неуниверсальным контейнерам, если для типа есть средство извлечения значений с аннотацией @UnwrapByDefault
.
Экстракторы значений — это классы, которые извлекают значения из контейнеров для проверки.
`` Эталонная реализация содержит экстракторы значений дляOptionalInt
, OptionalLong
иOptionalDouble :
@Min(1)
private OptionalInt numberOfOrders;
В этом случае аннотация @Min
применяется к обернутому целочисленному
значению, а не к контейнеру.
5. Пользовательские элементы контейнера
В дополнение к встроенным экстракторам значений мы также можем определить свои собственные и зарегистрировать их в типе контейнера.
Таким образом, мы можем добавлять аннотации проверки к элементам наших пользовательских контейнеров.
Давайте добавим новый класс Profile
, содержащий свойство companyName
:
public class Profile {
private String companyName;
// standard getters, setters
}
Затем мы хотим добавить свойство Profile
в класс Customer с аннотацией
@NotBlank
, которая проверяет, что companyName
не является пустой строкой
:
@NotBlank
private Profile profile;
Чтобы это работало, нам нужен экстрактор значений, который определяет проверку, которая будет применяться к свойству companyName
, а не непосредственно к объекту профиля.
Давайте добавим класс ProfileValueExtractor , реализующий интерфейс
ValueExtractor
и переопределяющий метод ExtractValue()
:
@UnwrapByDefault
public class ProfileValueExtractor
implements ValueExtractor<@ExtractedValue(type = String.class) Profile> {
@Override
public void extractValues(Profile originalValue,
ValueExtractor.ValueReceiver receiver) {
receiver.value(null, originalValue.getCompanyName());
}
}
В этом классе также необходимо указать тип извлекаемого значения с помощью аннотации @ExtractedValue
.
Кроме того `` , мы добавили аннотацию @UnwrapByDefault
, указывающую, что проверка должна применяться к развернутому значению, а не к контейнеру .
Наконец, нам нужно зарегистрировать класс, добавив файл с именем javax.validation.valueextraction.ValueExtractor
в каталог META-INF/services
, который содержит полное имя нашего класса ProfileValueExtractor
:
org.foreach.javaxval.container.validation.valueextractors.ProfileValueExtractor
Теперь, когда мы проверяем объект Customer
со свойством профиля
с пустым companyName
, мы увидим ошибку проверки:
@Test
public void whenProfileCompanyNameBlank_thenValidationFails() {
Customer customer = new Customer();
customer.setName("John");
Profile profile = new Profile();
profile.setCompanyName(" ");
customer.setProfile(profile);
Set<ConstraintViolation<Customer>> violations
= validator.validate(customer);
assertEquals(1, violations.size());
}
Обратите внимание, что если вы используете hibernate-validator-annotation-processor
, добавление аннотации проверки в пользовательский класс контейнера, когда он помечен как @UnwrapByDefault
, приведет к ошибке компиляции в версии 6.0.2.
Это известная проблема , которая, скорее всего, будет решена в следующей версии.
6. Заключение
В этой статье мы показали, как мы можем проверять несколько типов элементов контейнера с помощью Java Bean Validation 2.0.
Вы можете найти полный исходный код примеров на GitHub .