Suppose I have a sample entity class like this:
public class Address {
...
}
and a corresponding validator:
@Component
public AddressValidator implements Validator {
@Override
public boolean supports(Class<?> entityClass) {
return entityClass.equals(Address.class);
}
@Override
public void validate(Object obj, Errors errors) {
...
}
}
When I use a controller like the following, everything works:
@RestController
@RequestMapping("/addresses")
public class AddressController {
@Autowired
private AddressValidator validator;
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
@RequestMapping(method=POST)
public Long addNewAddress(@Valid @RequestBody Address address) {
...
}
}
However, if I omit the validator registering part (i.e. the following), validation is not performed.
@Autowired
private AddressValidator validator;
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
Having to register validators manually seems pointless. Can I instruct Spring to look up validators automatically (similar to how controllers are looked up)?
It's a Spring Boot based application.
You can use my example from gist or below. The idea is to have a main CompositeValidator that will be a holder of all your Validator or SmartValidator instances.
It supports hints and can be also integrate with Hibernate Annotation Validator (LocalValidatorFactoryBean). And also it's possible to have more that one validator per specific Model.
CompositeValidator.java
@Component
public class CompositeValidator implements SmartValidator {
@Autowired
private List<Validator> validators = Collections.emptyList();
@PostConstruct
public void init() {
Collections.sort(validators, AnnotationAwareOrderComparator.INSTANCE);
}
@Override
public boolean supports(Class<?> clazz) {
for (Validator validator : validators) {
if (validator.supports(clazz)) {
return true;
}
}
return false;
}
@Override
public void validate(Object target, Errors errors) {
validate(target, errors, javax.validation.groups.Default.class);
}
@Override
public void validate(Object target, Errors errors, Object... validationHints) {
Class<?> clazz = target.getClass();
for (Validator validator : validators) {
if (validator.supports(clazz)) {
if (validator instanceof SmartValidator) {
((SmartValidator) validator).validate(target, errors, validationHints);
} else {
validator.validate(target, errors);
}
}
}
}
}
SomeController.java
@Controller
@RequestMapping("/my/resources")
public class SomeController {
@RequestMapping(method = RequestMethod.POST)
public Object save(
@Validated(javax.validation.groups.Default.class) // this interface descriptor (class) is used by default
@RequestBody MyResource myResource
) { return null; }
}
Java Config
@Configuration
public class WebConfig {
/** used for Annotation based validation, it can be created by spring automaticaly and you don't do it manualy */
// @Bean
// public Validator jsr303Validator() {
// LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
// // validator.setValidationMessageSource(...);
// return validator;
// }
@Bean
public WebMvcConfigurerAdapter webMvcConfigurerAdapter() {
return new WebMvcConfigurerAdapter() {
@Autowired
private CompositeValidator validator;
@Override
public Validator getValidator() {
return validator;
}
}
}
Or XML Config
<!-- used for Annotation based validation, it can be created by spring automaticaly and you don't do it manualy -->
<!--<bean id="jsr303Validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">-->
<!-- <property name="validationMessageSource" ref="messageSource"/>-->
<!--</bean>-->
<mvc:annotation-driven validator="compositeValidator">
//...
</mvc:annotation-driven>
You can configure global Validator.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#validation-mvc
If you are using Java based spring configuration with WebMvcConfigurationSupport, you can override getValidator()
/**
* Override this method to provide a custom {@link Validator}.
*/
protected Validator getValidator() {
return null;
}
You may call setValidator(Validator) on the global WebBindingInitializer. This allows you to configure a Validator instance across all @Controller classes. This can be achieved easily by using the Spring MVC namespace:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven validator="globalValidator"/>
</beans>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With