简体   繁体   中英

Java Bean method constraint validation with Spring does not work for constructors

I'm currently trying to implement some method constraint validations, using Hibernate Validator . I have defined

  1. Parameter constraints for methods
  2. Return value constraints for methods
  3. Parameter constraints for constructors
  4. Return value constraints for constructors

The manual validation using the ExecutableValidator interface, as described in the Hibernate Validator documentation , works perfectly for 1.-4.

The integration with Spring basically works with annotating the bean with @Validated . When I try to validate my constraints in a Spring container with @Validated , only 1. and 2., ie validation of methods, works, but not for constructors (3. and 4.).

Any ideas, what is going wrong?

I created a minimum working example: The first test case is successful, even if it should thow an exception The second test case fails, because a ConstraintViolationException is thrown at the moment when the setter is called (actually, the exception should be thrown one line before, too).

My customer bean, which shall be validated (note: the setFirstName() has a @NotNull annotation for its parameter):

@Validated
public class Customer {

  private String firstName;

  private String lastName;

  public Customer(@NotNull String firstName, @NotNull String lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
  }

  public String getFirstName() {
      return firstName;
  }

  public void setFirstName(@NotNull String firstName) {
      this.firstName = firstName;
  }

  public String getLastName() {
      return lastName;
  }

  public void setLastName(String lastName) {
      this.lastName = lastName;
  }
}

The belonging config:

@Configuration
@ComponentScan({ "com.example.demo" })
public class MethodValidationConfig {

  @Bean
  public MethodValidationPostProcessor methodValidationPostProcessor() {
      return new MethodValidationPostProcessor();
  }

  @Bean("customer")
  @Scope(BeanDefinition.SCOPE_PROTOTYPE)
  public Customer customer(String firstName, String lastName) {

      Customer customer = new Customer(firstName, lastName);
      return customer;
  }
}

Then the two test cases. As said before, the first one doesn't throw an exception, even if it should. The second one throws an exception, but at the moment of calling the setter, and not before.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { MethodValidationConfig.class }, loader = 
AnnotationConfigContextLoader.class)
public class DemoApplicationTests {

  @Autowired
  private ApplicationContext applicationContext;

  @Test
  public void callConstructorWithInvalidParameter(){

      Customer c = (Customer) applicationContext.getBean("customer", null, null);
  }

  @Test
  public void callSetterWithInvalidParameter(){

      Customer c = (Customer) applicationContext.getBean("customer", "John", "Doe");
      c.setFirstName(null);
  }

}

And finally my pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
    http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.7.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator-annotation-processor</artifactId>
            <version>6.0.7.Final</version>
        </dependency>
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>javax.el</artifactId>
            <version>2.2.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
    <packaging>jar</packaging>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
</project>

Spring validation uses AOP. By default Spring AOP is used. Spring AOP is a runtime AOP implementation that proxies managed components (aka beans).

For this very same reason constructors cannot be advised with spring AOP and hence validation won't work.

For more sophisticated AOP do consider Aspectj. Or simply try to move validation to method execution. (the only supported join points in spring AOP).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM