验证程序两次调用“返回值约束”

时间:2019-06-28 18:37:08

标签: java spring-boot bean-validation

在Spring Boot项目中,我需要验证业务规则,而我正在尝试使用Bean验证来做到这一点。

我写了一个单独的类来放置我的业务规则,并使用“返回值约束”技术来实现。 但是,Validator.validate()方法调用了我的约束方法两次。

为什么?以及如何解决这个问题?

下面的简单代码更容易理解该问题:

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
    @Autowired
    private Validator validator;

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

    @Override
    public void run(String... args) throws Exception {
        DemoObject obj = new DemoObject();
        validator.validate(obj);
    }
}

class DemoObject {
    @AssertTrue(message="My business rule was failed")
    public boolean isMyBusinessRule() {
        System.out.println("isMyBusinessRule called");
        // ... my business rule validation code ...
        return true;
    }
}

方法isMyBusinessRule()被调用了两次。输出控制台显示:

isMyBusinessRule called
isMyBusinessRule called

如何解决这个问题?

1 个答案:

答案 0 :(得分:0)

我刚刚在Spring Boot 2.2上进行了测试,发现同样的事情。
现在,您甚至不应该考虑通过Hibernate Validator实现调用该方法一次,两次甚至更多次的事实。

仅出于记录目的,在处理验证之前第一次调用它以检查是否需要验证。
如果需要验证,则处理约束,然后第二次读取:从而再次调用带有注释的方法。
这里用我的3条评论方法来说明流程:

private boolean validateMetaConstraint(ValidationContext<?> validationContext, ValueContext<?, Object> valueContext, Object parent, MetaConstraint<?> metaConstraint) {
    // .... FIRST INVOCATION
    if ( isValidationRequired( validationContext, valueContext, metaConstraint ) ) {

        if ( parent != null ) {
           // .... SECOND INVOCATION with valueContext.getValue()
            valueContext.setCurrentValidatedValue( valueContext.getValue( parent, metaConstraint.getLocation() ) );
        }

        success = metaConstraint.validateConstraint( validationContext, valueContext );

        validationContext.markConstraintProcessed( valueContext.getCurrentBean(), valueContext.getPropertyPath(), metaConstraint );
    }
    // ....
}

该行为来自ValueContext class,该行为存储验证信息,并且出于优化或处理的原因,该行为可能会多次调用:

  

此类的一个实例用于收集所有相关的   验证单个类,属性或方法的信息   调用。

仍然忘记了它,这是实现的细节,明天在下一版本中,可以一次调用带注释的方法,并且永远不要破坏您的逻辑。因此,请不要依赖它。

重要的是,API遵守其合同并且做到了:即使实现两次调用了该方法,也将返回单个验证错误。

public class DemoObj {

    private final boolean value;

    DemoObj(boolean value){
        this.value = value;
    }
    @AssertTrue(message = "My business rule was failed")
    public boolean isMyBusinessRule() {
        System.out.println("isMyBusinessRule called");
        return value;
    }
}

使用它:

Set<ConstraintViolation<DemoObj>> constraintViolations = validator.validate(new DemoObj (true));
System.out.println("validations errors : " +  constraintViolations.size());

constraintViolations = validator.validate(new DemoObj (false));
System.out.println("validation errors : " +  constraintViolations.size());

输出:

isMyBusinessRule called
isMyBusinessRule called
validations errors : 0
isMyBusinessRule called
isMyBusinessRule called
validation errors : 1