Java 如何手动触发弹簧验证?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28702809/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 06:40:00  来源:igfitidea点击:

How to manually trigger spring validation?

javaspringspring-bootbean-validation

提问by Srini K

The annotated spring validation on fields of a POJO works when it is created from json request body. However, when I create the same object manually (using setters) and want to trigger validation, I'm not sure how to do that.

当从 json 请求正文创建时,POJO 字段上的带注释的 spring 验证工作。但是,当我手动创建相同的对象(使用 setter)并想要触发验证时,我不知道该怎么做。

Here is the Registration class, which has Builder inner class that can build the object. In the build method I would like to trigger spring validation. Please scroll to the bottom and check Builder.build() and Builder.valiate() methods to see current implementation. I'm using javax.validation.Validator to trigger validation, but I prefer to leverage spring validation if possible.

这是 Registration 类,它具有可以构建对象的 Builder 内部类。在构建方法中,我想触发弹簧验证。请滚动到底部并检查 Builder.build() 和 Builder.valiate() 方法以查看当前实现。我正在使用 javax.validation.Validator 来触发验证,但如果可能,我更喜欢利用 spring 验证。

package com.projcore.dao;

import com.projcore.util.ToString;
import com.fasterxml.Hymanson.annotation.JsonIgnoreProperties;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.constraints.Size;
import java.util.List;
import java.util.Set;

/**
 * The data transfer object that contains the information of a Registration
 * and validation rules for attributes.
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public final class Registration {
    private static final Logger LOGGER = LoggerFactory.getLogger(Registration.class);

    private String id;

    @NotEmpty
    @Size(max = 255)
    private String messageId;

    @NotEmpty
    @Size(max = 255)
    private String version;

    @Size(max = 255)
    private String system;

    public Registration() {
    }

    private Registration(Builder builder) {
        this.id = builder.id;
        this.messageId = builder.messageId;
        this.version = builder.version;
        this.system = builder.system;
    }

    public static Builder getBuilder() {
        return new Builder();
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getMessageId() {
        return messageId;
    }

    public void setMessageId(String messageId) {
        this.messageId = messageId;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getSystem() {
        return system;
    }

    public void setSystem(String system) {
        this.system = system;
    }

    @Override
    public String toString() {
        return ToString.create(this);
    }

    /**
     * Builder pattern makes the object easier to construct in one line.
     */
    public static class Builder {

        private String id;

        private String messageId;

        private String version;

        private String system;

        private Builder() {}

        public Builder id(String id) {
            this.id = id;
            return this;
        }

        public Builder messageId(String messageId) {
            this.messageId = messageId;
            return this;
        }


        public Builder version(String version) {
            this.version = version;
            return this;
        }

        public Builder system(String system) {
            this.system = system;
            return this;
        }

        public Registration build() {
            Registration entry = new Registration(this);

            // *** Would like to trigger spring validation here ***
            Set violations = validate(entry);
            if (violations.isEmpty())
                return entry;
            else
                throw new RuntimeException(violations.toString());
        }

        private Set validate(Registration entry) {
            Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
            Set<ConstraintViolation<Registration>> constraintViolations = validator.validate(entry);
            return constraintViolations;
        }

    }
}

Validation works fine here:

验证在这里工作正常:

@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
Registration create(@RequestBody @Valid Registration registration) 


Solution:

解决方案:

Removed Registraion.Builder.validate(). Updated Registraion.Builder.build() to:

删除了 Registraion.Builder.validate()。将 Registraion.Builder.build() 更新为:

    public Registration build() {
        Registration entry = new Registration(this);
        return (Registration) ValidatorUtil.validate(entry);
    }

ValidationUtil.java

验证实用程序

package projcore.util;

import com.ericsson.admcore.error.InvalidDataException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;

import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;

public class ValidatorUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(ValidatorUtil.class);
    private static final Validator javaxValidator = Validation.buildDefaultValidatorFactory().getValidator();
    private static final SpringValidatorAdapter validator = new SpringValidatorAdapter(javaxValidator);

    public static Object validate(Object entry) {
        Errors errors = new BeanPropertyBindingResult(entry, entry.getClass().getName());
        validator.validate(entry, errors);
        if (errors == null || errors.getAllErrors().isEmpty())
            return entry;
        else {
            LOGGER.error(errors.toString());
            throw new InvalidDataException(errors.getAllErrors().toString(), errors);
        }
    }
}

InvalidDataException.java

无效数据异常.java

package projcore.error;

import org.springframework.validation.Errors;

/**
 * This exception is thrown when the dao has invalid data.
 */
public class InvalidDataException extends RuntimeException {
    private Errors errors;

    public InvalidDataException(String msg, Errors errors) {
        super(msg);
        setErrors(errors);
    }

    public Errors getErrors() {
        return errors;
    }

    public void setErrors(Errors errors) {
        this.errors = errors;
    }
}

采纳答案by iamiddy

Spring provides full support for the JSR-303 Bean Validation API. This includes convenient support for bootstrapping a JSR-303 implementation as a Spring bean. This allows a javax.validation.Validator to be injected wherever validation is needed in your application.

Spring 完全支持 JSR-303 Bean Validation API。这包括对将 JSR-303 实现引导为 Spring bean 的便利支持。这允许在应用程序中需要验证的任何地方注入 javax.validation.Validator。

Use the LocalValidatorFactoryBean to configure a default JSR-303 Validator as a Spring bean:

使用 LocalValidatorFactoryBean 将默认 JSR-303 验证器配置为 Spring bean:

   <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />

The basic configuration above will trigger JSR-303 to initialize using its default bootstrap mechanism. A JSR-303 provider, such as Hibernate Validator, is expected to be present in the classpath and will be detected automatically.

上面的基本配置将触发 JSR-303 使用其默认引导机制进行初始化。JSR-303 提供程序,例如 Hibernate Validator,预计会出现在类路径中,并且会被自动检测到。

5.7.2.1 Injecting a Validator

5.7.2.1 注入验证器

LocalValidatorFactoryBean implements both javax.validation.Validator and org.springframework.validation.Validator.You may inject a reference to one of these two interfaces into beans that need to invoke validation logic.

LocalValidatorFactoryBean implements both javax.validation.Validator and org.springframework.validation.Validator.您可以将这两个接口之一的引用注入到需要调用验证逻辑的 bean 中。

Inject a reference to javax.validation.Validatorif you prefer to work with the JSR-303 API directly:

javax.validation.Validator如果您更喜欢直接使用 JSR-303 API,请注入一个参考:

// JSR-303 Validator
import javax.validation.Validator;

@Service
public class MyService {

    @Autowired
    private Validator validator;

}

Inject a reference to org.springframework.validation.Validatorif your bean requires the Spring Validation API:

org.springframework.validation.Validator如果您的 bean 需要 Spring Validation API ,则注入一个引用:

// Spring Validator
import org.springframework.validation.Validator;

@Service
public class MyService {

    @Autowired
    private Validator validator;

}

Here is a well exaplained example Using JSR 303 with "classic" Spring Validators (enter the SpringValidatorAdapter)

这是一个经过充分解释的示例 Using JSR 303 with "classic" Spring Validators(输入 SpringValidatorAdapter)

This link is very helpful. Wrapping javax.validation.Validator in org.springframework.validation.beanvalidation.SpringValidatorAdapter helped deal with errors consistently. Can you add this as an answer so that I can accept it

这个链接非常有帮助。在 org.springframework.validation.beanvalidation.SpringValidatorAdapter 中包装 javax.validation.Validator 有助于一致地处理错误。您能否将此添加为答案,以便我可以接受

and Spring doc here

Spring 文档在这里