CodeGym/Java Course/Module 5. Spring/Java Bean Validation

Java Bean Validation

Available

Bean Validation in Brief

Spring Framework provides support for the Java Bean Validation

Bean Validation provides a common way to check validity by declaring constraints and metadata for Java applications. To use it, you annotate domain model properties with declarative validation constraints, which are then enforced by the runtime. There are built-in constraints, but you can also define your own constraints.

Consider the following example, which shows a simple PersonForm model with two properties:

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

Bean Validation allows you to declare constraints, as shown in the following example:

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
)

The Bean Validation validator then validates instances of this class based on the declared constraints. For general information about this API, see "Bean Validation". See the Hibernate Validator documentation for specific restrictions. To learn how to configure a bean validation provider as a Spring bean, continue reading.

Configuring a Bean Validation Provider

Spring provides full support for the Bean Validation API, including bootstrapping the Bean provider Validation as a Spring bean. This allows you to implement javax.validation.ValidatorFactory or javax.validation.Validator wherever validation is required in your application.

You can use LocalValidatorFactoryBean to configure the default validator as a Spring bean, as shown in the following example:

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"/>

The basic configuration in the previous example causes bean validation to be initialized using the default loading mechanism. A Bean Validation provider, such as the Hibernate Validator, must be on the classpath and be detected automatically.

Validator Injection

LocalValidatorFactoryBean implements as javax.validation .ValidatorFactory and javax.validation.Validator, as well as org.springframework.validation.Validator from Spring. You can embed a reference to any of these interfaces in beans that should call the validation logic.

You can embed a reference to javax.validation.Validator if you prefer to work directly with the API -Bean Validation interface, as shown in the following example:

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)

You can embed a reference to org.springframework.validation.Validator , if the bean requires the Spring Validation API, as shown in the following example:

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)

Setting up custom constraints

Each bean validation constraint consists of two parts:

  • The @Constraint annotation, which declares the constraint and its custom properties.

  • Implementations of the javax.validation.ConstraintValidator interface, which implements the logic behind the constraint.

To associate the declaration with the implementation, each @Constraint annotation references the corresponding ConstraintValidator implementation class. At runtime, ConstraintValidatorFactory instantiates an implementation that is referenced if the constraint annotation occurs in your domain model.

By default, LocalValidatorFactoryBean configures SpringConstraintValidatorFactory, which uses Spring to create ConstraintValidator instances. This allows custom ConstraintValidators to take advantage of dependency injection just like any other Spring bean.

The following example shows a custom @Constraint declaration followed by the associated implementation ConstraintValidator, which uses Spring for dependency injection:

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 {
    // ...
}

As follows from the previous example, the ConstraintValidator implementation may have its own dependencies, marked with the @Autowired annotation, just like any other Spring bean.

Spring-managed method validation

You can implement the method validation functionality supported by Bean Validation 1.1 (and, as a special extension, also Hibernate Validator 4.3), into the Spring context via the definition of the 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"/>

To implement Spring-managed method validation, all target classes must be annotated with the @Validated annotation from Spring, in which can optionally also declare validation groups for use. See MethodValidationPostProcessor for more details on configuring using Hibernate Validator and Bean Validation 1.1 providers.

Method validation uses AOP proxies to bypass target classes, either dynamic proxies from the JDK for methods on interfaces, or proxies from CGLIB. There are certain restrictions when using a proxy. Also, remember that you should always use methods and accessors for proxied classes; direct access to fields will not work.

Additional configuration options

The default LocalValidatorFactoryBean configuration is sufficient for most cases. There are a number of configuration options for various Bean Validation constructs, from message interpolation to enabling traversal. See javadoc at LocalValidatorFactoryBean for more information about these options.

Configuring DataBinder

As of Spring 3, you can configure a DataBinder instance using the Validator. Once configured, you can call Validator by calling binder.validate(). Any Errors validity checks are automatically added to the BindingResult binding.

The following example shows how to programmatically use the DataBinder to call logic validation checks after binding to the target:

Java
Foo target = new Foo();
DataBinder binder = new DataBinder(target);
binder.setValidator(new FooValidator());
// bind to the target object
binder.bind(propertyValues);
// validate the target
object binder.validate();
// get the BindingResult, including all validation errors
BindingResult results = binder.getBindingResult();
Kotlin
val target = Foo()
val binder = DataBinder(target)
binder.validator = FooValidator()
// bind to the target object
binder.bind(propertyValues)
// validate the target object
binder.validate()
// get the BindingResult, which includes everything validation errors
val results = binder.bindingResult

You can also configure DataBinder using multiple instances of Validator via dataBinder.addValidators and dataBinder.replaceValidators. This makes sense when combining globally configured bean validation with a Validator from Spring configured locally for a DataBinder instance.

Comments
  • Popular
  • New
  • Old
You must be signed in to leave a comment
This page doesn't have any comments yet