Search code examples
springbean-validationhibernate-validatorspring-framework-beansjakarta-validation

Why isn't Spring kicking in the JSR-303 bean validation?


Wondering why Spring is not getting the Hibernate Bean Validation kicked in. According to the documentation HERE, it requires a MethodValidationPostProcessor in the context and the target spring bean to be annotated with @Validated annotation. I got both of these requirements done, but it is still not working.

I expected an exception at the call to backgroundCheck method due to the fact that the person's name is null and the age is less than 18. For some reason, however, the Person object is not being validated and the method finishes without exceptions.

Here is my setup:

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;

@SpringBootApplication
public class Application implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public LocalValidatorFactoryBean validator() {
        return new LocalValidatorFactoryBean();
    }

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
        methodValidationPostProcessor.setValidator(validator());
        return methodValidationPostProcessor;
    }

    @Override
    public void run(String... args) {

        PersonService personService = new PersonService();

        try {
            personService.backgroundCheck(new Person(null, 4));
        } catch (Exception e) {
            System.out.println("<<< EXCEPTION >>>");
            return;
        }

        System.out.println("<<< NO VALIDATIONS THROWN >>>");
    }
}
import jakarta.validation.Valid;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

@Validated
@Service
public class PersonService {

    public void backgroundCheck(@Valid Person person) {
        System.out.printf("Running background check for %s%n", person.name());
    }
}
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;

public record Person(
        @NotBlank(message = "Name must not be blank")
        String name,

        @Min(value = 18, message = "Age should not be less than 18")
        int age) {
}
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.3</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <java.version>21</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Any idea what I am missing?


Solution

    • org.springframework.validation.annotation.Validated @Validated must be a Spring Bean to perform validation.
      • This is an AOP-based method that intercepts and processes method requests.
      • Therefore, validation can be performed on any Spring bean regardless of the hierarchy such as controller, service, or repository.
    • The problem is that the code simply creates and uses a PersonService instance.
      • Currently, PersonService created through the new keyword is not a Spring Bean.
      • So @Validated also has no effect.
    • In the code below, I will change the code to make it work as expected.
    @SpringBootApplication
    public class Application implements CommandLineRunner {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
        
        @Bean
        public LocalValidatorFactoryBean validator() {
            return new LocalValidatorFactoryBean();
        }
    
        @Bean
        public MethodValidationPostProcessor methodValidationPostProcessor() {
            MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
            methodValidationPostProcessor.setValidator(validator());
            return methodValidationPostProcessor;
        }
        // register spring bean!
        @Bean
        public PersonService personService() {
            return new PersonService();
        }
    
        @Override
        public void run(String... args) {
    
            try {
                personService().backgroundCheck(new Person(null, 4));
            } catch (ConstraintViolationException e) {
                System.out.println("<<< EXCEPTION >>>");
                return;
            }
            System.out.println("<<< NO VALIDATIONS THROWN >>>");
        }
    }
    

    Result.

    2024-03-20T13:09:23.676+09:00  INFO 4384 --- [           main]
    <<< EXCEPTION >>>
    
    • ConstraintViolationException occurred normally.

    I hope it will be of help :) thanks.