3

I've noticed that JSR-303 validation is completely ignored in Spring when a custom Validator bean annotated with @Component is declared. Interestingly enough said custom validator doesn't even have to filled in or used by any of the classes. The fact that its component scanned by Spring appears to be enough to make Spring skip JSR-303 validation during object binding altogether. Removing @Component from custom Validator and restarting web application enables JSR-303 validation as expected. Annotating custom validators with @Component has its uses eg to have Spring Autowire dependencies.

Consider simple example below:

/* Simple JSR-303 annotated User object */
public class User {
    
    @NotNull @Size(min = 2, max = 5)
    protected String username;
    
    @Size(min = 2, max = 32)
    protected String firstName;
    
    @Size(min = 2, max = 32)
    protected String lastName;
    
    @NotNull @Past @DateTimeFormat(pattern="dd/MM/yyyy")
    protected Date dateOfBirth;
    
    @NotNull @Email
    protected String email;
    
    protected String phone;
    
    //getters and setters
}
/* Controller example */
@RestController
public class UserController {
    
    @PostMapping("/users/register")
    public ResponseEntity postUser(@Valid @RequestBody User user, BindingResult result) {
        if (result.hasErrors()) {
            return new ResponseEntity(result.getAllErrors(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return new ResponseEntity(user, HttpStatus.CREATED);
    }
    
}
/* Custom validator (doesn't even have to be in use) */
@Component //commenting out @Component annotation enables JSR-303 again
public class SomeValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        //just an example
        return false;
    }
    
    @Override
    public void validate(Object target, Errors errors) {
        //empty
    }
}

I was scratching my head over this one and couldn't figure out why my JSR-303 objects weren't validated but I managed to narrow it down and replicate this with a Spring Boot project containing above classes. Why is that? Am I missing something here or is this a Spring bug?


Edit

See demo Spring Boot project on GitHub

4
  • You're using Hibernate validator I assume? Commented May 25, 2017 at 20:17
  • Can you put your sample project up someplace? I've got a Spring Boot 1.5.3 project with Hibernate validator 5.4.1 and I can't replicate this problem. I've even used applicationContext.getBeansOfType to verify that the @Component annotation results in a Spring bean being created. Commented May 25, 2017 at 20:47
  • @ngreen Yes, Hibernate Validator. Default pulled by Spring Boot so org.hibernate:hibernate-validator:5.3.5.Final to be exact. I uploaded demo project to GitHub. See updated post. Commented May 25, 2017 at 21:47
  • Your SomeValidator is part of the Spring Validation API, maybe you have to configure that Spring uses both validation APIs together? see stackoverflow.com/a/26733582/649686 for an example Commented May 26, 2017 at 9:11

1 Answer 1

2

So there are a number of problems going on in your code. First, your custom validator doesn't support anything, so it's never going to be invoked. You probably realize that, so I'll leave it up to you to fix it.

Your real problem, though, is that by creating a validator bean like that, Spring will not create the defaultValidator bean (which is a LocalValidatorFactoryBean) nor will it create the methodValidationPostProcessor bean (which is needed to validate method parameters). This is a common problem people run into with Spring: once you do something to disturb the auto-configure process, you have to define things manually. The solution is simple: create a config class that defines these beans. Example:

@Configuration
public class ValidationConfig {
    @Bean public LocalValidatorFactoryBean defaultValidator() {
        return new LocalValidatorFactoryBean();
    }

    @Bean public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

I worked my way around it by defining custom validation annotations instead but I just tried your suggestion and it appears to be working flawlessly. So the fact that a Bean implementing Validator interface appears in context makes Spring cancel entire auto-configuration of validators?
That would appear to be the case, yes. You can check out the source of org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration to see how things work. I haven't had time to analyze it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.