Кратко о Bean Validation

Spring Framework обеспечивает поддержку API-интерфейса Java Bean Validation

Bean Validation обеспечивает общий способ проверки достоверности с помощью объявления ограничений и метаданных для Java-приложений. Чтобы использовать его, вы аннотируете свойства модели предметной области декларативными ограничениями проверки достоверности, которые затем применяются средой выполнения. Существуют встроенные ограничения, но вы также можете определить свои собственные ограничения.

Рассмотрим следующий пример, в котором показана простая модель PersonForm с двумя свойствами:

Java
public class PersonForm {
    private String name;
    private int age;
}
Kotlin
class PersonForm(
        private val name: String,
        private val age: Int
)

Bean Validation позволяет объявлять ограничения, как показано в следующем примере:

Java
public class PersonForm {
    @NotNull
    @Size(max=64)
    private String name;
    @Min(0)
    private int age;
}
Kotlin
class PersonForm(
    @get:NotNull @get:Size(max=64)
    private val name: String,
    @get:Min(0)
    private val age: Int
)

Затем валидатор Bean Validation проверяет экземпляры этого класса, основываясь на объявленных ограничениях. Общую информацию о данном API-интерфейсе см. в разделе "Bean Validation". Конкретные ограничения см. в документации по Hibernate Validator. Чтобы узнать, как настроить поставщика проверки достоверности бинов в качестве бина Spring, продолжайте читать.

Конфигурирование поставщика Bean Validation

Spring обеспечивает полную поддержку API-интерфейса Bean Validation, включая начальную загрузку поставщика Bean Validation в качестве бина Spring. Это позволяет внедрять javax.validation.ValidatorFactory или javax.validation.Validator всюду, где требуется проверка достоверности в приложении.

Вы можете использовать LocalValidatorFactoryBean для настройки валидатора по умолчанию в качестве бина Spring, как показано в следующем примере:

Java
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
@Configuration
public class AppConfig {
    @Bean
    public LocalValidatorFactoryBean validator() {
        return new LocalValidatorFactoryBean();
    }
}
XML
<bean id="validator"
    class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

Базовая конфигурация в предыдущем примере вызывает инициализацию проверки достоверности бинов, используя механизм загрузки по умолчанию. Поставщик Bean Validation, такой как Hibernate Validator, должен находиться в пути классов и определяться автоматически.

Внедрение валидатора

LocalValidatorFactoryBean реализует как javax.validation.ValidatorFactory и javax.validation.Validator, так и org.springframework.validation.Validator из Spring. Вы можете внедрить ссылку на любой из этих интерфейсов в бины, которые должны вызывать логику проверки достоверности.

Можно внедрить ссылку на javax.validation.Validator, если предпочтение отдается прямой работе с API-интерфейсом Bean Validation, как показано в следующем примере:

Java
import javax.validation.Validator;
@Service
public class MyService {
    @Autowired
    private Validator validator;
}
Kotlin
import javax.validation.Validator;
@Service
class MyService(@Autowired private val validator: Validator)

Можно внедрить ссылку на org.springframework.validation.Validator, если бин требует API-интерфейс Spring Validation, как показано в следующем примере:

Java
import org.springframework.validation.Validator;
@Service
public class MyService {
    @Autowired
    private Validator validator;
}
Kotlin
import org.springframework.validation.Validator
@Service
class MyService(@Autowired private val validator: Validator)

Настройка специальных ограничений

Каждое ограничение проверки достоверности бинов состоит из двух частей:

  • Аннотации @Constraint, которая объявляет ограничение и его настраиваемые свойства.

  • Реализации интерфейса javax.validation.ConstraintValidator, который реализует логику работы ограничения.

Чтобы связать объявление с реализацией, каждая аннотация @Constraint ссылается на соответствующий класс реализации ConstraintValidator. Во время выполнения ConstraintValidatorFactory создает экземпляр реализации, на которую ссылаются, если аннотация ограничения встречается в вашей модели предметной области.

По умолчанию LocalValidatorFactoryBean конфигурирует SpringConstraintValidatorFactory, которая использует Spring для создания экземпляров ConstraintValidator. Это позволяет специальным ConstraintValidators пользоваться внедрением зависимостей, как и любым другим бином Spring.

В следующем примере показано специальное объявление @Constraint, за которым следует связанная реализация ConstraintValidator, использующая Spring для внедрения зависимостей:

Java
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=MyConstraintValidator.class)
public @interface MyConstraint {
}
Kotlin
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
@Constraint(validatedBy = MyConstraintValidator::class)
annotation class MyConstraint
Java
import javax.validation.ConstraintValidator;
public class MyConstraintValidator implements ConstraintValidator {
    @Autowired;
    private Foo aDependency;
    // ...
}
Kotlin
import javax.validation.ConstraintValidator
class MyConstraintValidator(private val aDependency: Foo) : ConstraintValidator {
    // ...
}

Как следует из предыдущего примера, реализация ConstraintValidator может иметь свои зависимости, помеченные аннотацией @Autowired, как и любой другой бин Spring.

Проверка достоверности методов, управляемая Spring

Вы можете внедрить функцию проверки методов, поддерживаемую Bean Validation 1.1 (и, в качестве специального расширения, также Hibernate Validator 4.3), в контекст Spring через определение бина MethodValidationPostProcessor:

Java
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@Configuration
public class AppConfig {
    @Bean
    public MethodValidationPostProcessor validationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}
XML
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>

Чтобы осуществлять проверку достоверности методов, управляемую Spring, все целевые классы должны быть аннотированы аннотацией @Validated из Spring, в которой по желанию можно также объявить группы поверки достоверности для использования. См. MethodValidationPostProcessor для получения более подробной информации о настройке с использованием поставщиков Hibernate Validator и Bean Validation 1.1.

Проверка достоверности методов использует АОП-прокси в обход целевых классов, либо динамические прокси из JDK для методов на интерфейсах, либо прокси из CGLIB. Существуют определенные ограничения при использовании прокси. Кроме того, помните, что для проксированных классов всегда нужно использовать методы и акцессоры; прямой доступ к полям работать не будет.

Дополнительные параметры конфигурации

Конфигурация LocalValidatorFactoryBean по умолчанию достаточна для большинства случаев. Существует ряд опций конфигурации для различных конструкций Bean Validation, от интерполяции сообщений до разрешения обхода. См. javadoc по LocalValidatorFactoryBean для получения дополнительной информации об этих опциях.

Конфигурирование DataBinder

Начиная со Spring 3, вы можете конфигурировать экземпляр DataBinder с помощью Validator. После конфигурирования можно вызывать Validator, обратившись к binder.validate(). Любые Errors проверки достоверности автоматически добавляются к привязке BindingResult.

В следующем примере показано, как программно использовать DataBinder для вызова логики проверки достоверности после привязывания к целевому объекту:

Java
Foo target = new Foo();
DataBinder binder = new DataBinder(target);
binder.setValidator(new FooValidator());
// привязываем к целевому объекту
binder.bind(propertyValues);
// валидируем целевой объект
binder.validate();
// получаем результат BindingResult, включающий все ошибки валидации
BindingResult results = binder.getBindingResult();
Kotlin
val target = Foo()
val binder = DataBinder(target)
binder.validator = FooValidator()
// привязываем к целевому объекту
binder.bind(propertyValues)
// валидируем целевой объект
binder.validate()
// получаем результат BindingResult, включающий все ошибки валидации
val results = binder.bindingResult

Также можно настроить DataBinder с использованием нескольких экземпляров Validator через dataBinder.addValidators и dataBinder.replaceValidators. Это имеет смысл при комбинировании глобально сконфигурированной валидации бинов с использованием Validator из Spring, настроенного локально для экземпляра DataBinder.