Here is a link to a very good site that explains how you can combine the JSR-303 validator with the spring validator.
I will present the following my solution, which works. Hope this helps.
My abstract validator:
import java.util.Map; import java.util.Set; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorFactory; import javax.validation.ConstraintViolation; import javax.validation.Validator; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.validation.Errors; public abstract class AbstractValidator implements org.springframework.validation.Validator, ApplicationContextAware, ConstraintValidatorFactory { @Autowired private Validator validator; private ApplicationContext applicationContext; public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) { Map<String, T> beansByNames = applicationContext.getBeansOfType(key); if (beansByNames.isEmpty()) { try { return key.newInstance(); } catch (InstantiationException e) { throw new RuntimeException("Could not instantiate constraint validator class '" + key.getName() + "'", e); } catch (IllegalAccessException e) { throw new RuntimeException("Could not instantiate constraint validator class '" + key.getName() + "'", e); } } if (beansByNames.size() > 1) { throw new RuntimeException("Only one bean of type '" + key.getName() + "' is allowed in the application context"); } return (T) beansByNames.values().iterator().next(); } public boolean supports(Class<?> c) { return true; } public void validate(Object objectForm, Errors errors) { Set<ConstraintViolation<Object>> constraintViolations = validator.validate(objectForm); for (ConstraintViolation<Object> constraintViolation : constraintViolations) { String propertyPath = constraintViolation.getPropertyPath().toString(); String message = constraintViolation.getMessage(); errors.rejectValue(propertyPath, "", message); } addExtraValidation(objectForm, errors); } protected abstract void addExtraValidation(Object objectForm, Errors errors);
}
Valid Validator:
import org.springframework.stereotype.Component; import org.springframework.validation.Errors; import ro.scorpionsoftware.demo3.dao.AbstractValidator; @Component(value="doctorValidator") public class DoctorValidator extends AbstractValidator { @Override protected void addExtraValidation(Object objectForm, Errors errors) {
Controller: (At the end, this is @Valid binding to the validator)
@Controller public class DoctorEditController { @Autowired private DoctorValidator doctorValidator; @RequestMapping(value = "/doctorEdit", method = RequestMethod.POST) public String processSubmit( @ModelAttribute("doctorForm") @Valid DoctorForm df, BindingResult result, ModelMap model) { ... } @InitBinder protected void initBinder(WebDataBinder binder) { binder.setValidator(doctorValidator); } }
In the context, declare the JSR-303 validator:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
With this approach, you can get the context both in the actual validator and in any other custom annotation that you want to implement.