Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using JSR-303 with custom validation

Dear Spring community,

What I am trying to implement is the following:

  • I would like to have a custom validator per controller (via @InitBinder)
  • I would like Spring to call validator.validate() (so not this way)
  • I would like to use JSR-303 @Valid annotation for that
  • The bean to be validated (RegistrationForm) does not have any per-field JSR-303 annotations
  • I don't want to include validation implementation (like Hibernate) into classpath; it will be useless as from above statement

I basically follow the steps mentioned here:

  • I add javax.validation.validation-api:validation-api as my dependency
  • I use <mvc:annotation-driven />
  • I mark my model with @Valid: public String onRegistrationFormSubmitted(@ModelAttribute("registrationForm") @Valid RegistrationForm registrationForm, BindingResult result) ...

So what happens, is that validation API tries to locate any implementation and fails:

Caused by: javax.validation.ValidationException: Unable to find a default provider
    at javax.validation.Validation$GenericBootstrapImpl.configure(Validation.java:264)
    at org.springframework.validation.beanvalidation.LocalValidatorFactoryBean.afterPropertiesSet(LocalValidatorFactoryBean.java:183)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)

The way out is to define a validator property for AnnotationDrivenBeanDefinitionParser:

<bean name="validator" class="org.company.module.RegistrationFormValidator" />

<mvc:annotation-driven validator="validator" />

but this approach means that the validator will be set to all controllers by ConfigurableWebBindingInitializer.initBinder().

I understand that I am trying to use the framework in a special way, but what the community will say, if there is a special meaning for validator property which tells that validator does not need to be resolved, e.g.

<mvc:annotation-driven validator="manual" />

with special treatment:

--- AnnotationDrivenBeanDefinitionParser.java.orig      2011-06-30 14:33:10.287577300 +0200
+++ AnnotationDrivenBeanDefinitionParser.java   2011-06-30 14:34:27.897449000 +0200
@@ -152,6 +152,10 @@

        private RuntimeBeanReference getValidator(Element element, Object source, ParserContext parserContext) {
                if (element.hasAttribute("validator")) {
+                       if ("manual".equals(element.getAttribute("validator"))) {
+                               return null;
+                       }
+
                        return new RuntimeBeanReference(element.getAttribute("validator"));
                }
                else if (jsr303Present) {

Any feedback is welcomed.

P.S. Repost from Spring Forum.

like image 578
dma_k Avatar asked Dec 04 '25 02:12

dma_k


2 Answers

This is also a repost of my answer/workaround on the above mentioned forum. Anyway I think it might help having it here as well.

The only workaround I found was to implement my own @Valid annotation, once Spring (at least in 3.1.1.RELEASE code base) only checks the method argument annotation's simple name (please look into the org.springframework.web.method.annotation.ModelAttributeMethodProcessor class below). This way, I don't need to add javax.validation.validation-api:validation-api to my project's dependencies and I stop getting the infamous javax.validation.ValidationException: Unable to find a default provider.

/**
 * Validate the model attribute if applicable.
 * <p>The default implementation checks for {@code @javax.validation.Valid}.
 * @param binder the DataBinder to be used
 * @param parameter the method parameter
 */
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
    Annotation[] annotations = parameter.getParameterAnnotations();
    for (Annotation annot : annotations) {
        if (annot.annotationType().getSimpleName().startsWith("Valid")) {
            Object hints = AnnotationUtils.getValue(annot);
            binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
        }
    }
}
like image 188
mhnagaoka Avatar answered Dec 06 '25 16:12

mhnagaoka


Why you don't want to include Hibernate Validator? Ever JSR specification have some implementations, you can not work with the specification without any implementation (or provider).

Can you imagine working with JDBC without any JDBC driver? Working with JPA without a provider? Working with Servlets without any container?

It's just the same, Hibernate Validator is the reference implementation of JSR-303, I'm not aware of any other implementation, but if you don't like Hibernate Validator, just go for another implementation.

like image 34
Amir Pashazadeh Avatar answered Dec 06 '25 16:12

Amir Pashazadeh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!