Java 使用 Spring 的 Bean 方法约束验证不适用于构造函数

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

我目前正在尝试使用 Hibernate Validator 实现一些方法约束验证。 我已经定义

  1. 方法的参数约束
  2. Return 方法的值约束
  3. 构造函数的参数约束
  4. Return 构造函数的值约束

使用 ExecutableValidator 接口的手动验证,如 Hibernate Validator documentation 中所述,非常适合 1.-4.

与 Spring 的集成基本上与使用 @Validated 注释 bean 一起工作。 当我尝试使用 @Validated 在 Spring 容器中验证我的约束时,只有 1. 和 2.,我。 e.方法验证有效,但不适用于构造函数(3. 和 4.)。

任何想法,出了什么问题?

我创建了一个最小工作示例: 第一个测试用例是成功的,即使它应该抛出异常 第二个测试用例失败,因为在调用 setter 的那一刻抛出了 ConstraintViolationException(实际上,该异常也应该在前一行抛出)。

我的客户 bean,应进行验证(注意:setFirstName() 的参数有一个 @NotNull 注释):

@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;
  }
}

所属配置:

@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;
  }
}

然后是两个测试用例。 如前所述,第一个不会抛出异常,即使它应该抛出。 第二个抛出异常,但在调用 setter 的那一刻,而不是之前。

@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);
  }

}

最后是我的 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 验证使用 AOP。默认使用 Spring AOP。 Spring AOP 是一种运行时 AOP 实现,它代理托管组件(也称为 bean)。

出于同样的原因,无法使用 spring AOP 建议构造函数,因此验证将不起作用。

对于更复杂的 AOP,请考虑 Aspectj。或者只是尝试将验证移至方法执行。 (spring AOP 中唯一支持的连接点)。