Spring MVC 3 has a very rich set of features for binding and validating input data to command objects, including class-based validation and annotation-based validation.
Custom class-based validators can be registered with Spring MVC in one of two ways (as described in the documentation): configured in each @Controller class using the @InitBinder method, or configured at the application level using the "mvc:annotation-driven" tag with a "validator" argument. Using the per-controller approach gives lots of control when each Controller needs its own validators, but adding all those @InitBinder methods can become tedious (and therefore error-prone).
In a previous post, I showed a method for creating a meta-validator that could be configured as the global application-level validator, and would in turn delegate to other configured validators as appropriate for the command object type. This post is a refinement if that idea, giving more control over which validators are automatically discovered and globally available.
This flexibility is achieved by using annotations on the validators that should be globally available through the meta-validator, as in this example (stolen from the Spring documentation):
The first line is the part that is new - the @ValidatorComponent annotation. I don't love the name, but "Validator" was already taken and having the interface and annotation share a name would be confusing.
With the supporting code below, any classes that extend org.springframework.validation.Validator and also have the @ValidatorComponent annotation on them are automatically detected by the meta-validator and called when appropriate. Validators without the annotation are ignored by the meta-validator, and can be configured individually in @InitBinder methods if required.
Here is the code that makes this work. First, the annotation itself:
This annotation is annotated with @Component, which means validators using this annotation are also eligible for component scanning.
And, at last, the meta-validator class:
Using @Autowired with a collection as a way to get all beans of a certain type was a fairly recent discovery for me, and I like it much better than the older method of extending ApplicationContextAware and asking the ApplicationContext for "beansOfType". Otherwise this code is pretty straight-forward - it just scans the list of injected Validator beans and remembers any that have the annotation on them.
The setValidators() method is provided for the case where third-party validators also need to be registered with the meta-validator, such as the LocalValidatorFactoryBean needed to configure JSR-303 bean validation support.
Here is an example of a Spring XML configuration file that wires all this together:
That's it! Now all you have to do is write your validator classes (and their unit tests), annotate them with @ValidatorComponent, and let the framework do the rest.
Very nice post. I like the idea of the @ValidatorComponent that gets registered automatically!
ReplyDelete