Hibernate Validator

本文主要講述如何使用hibernate validator來校驗入參,避免在業務代碼裏進行每一個接口進行入參校驗,提搞代碼的簡潔及欣賞性。 主要涉及針對接口方法的入參簡單校驗,不涉及使用group進行的組合校驗及其餘。java

1、POM依賴

<!-- dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.13.Final</version>
</dependency>
<dependency>
    <groupId>javax.el</groupId>
    <artifactId>javax.el-api</artifactId>
    <version>3.0.1-b06</version>
</dependency>
<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>javax.el</artifactId>
    <version>2.2.6</version>
</dependency>

注:hibernate-validator-6.0.13.Final自己依賴validation-api的版本便是2.0.1.Final,若無衝突,則無需單獨顯式依賴validation-api-2.0.1.Finalweb

2、初始化Validator

2.1 方法一 註冊Bean實現

import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

@Configuration
public class ValidatorConfig {

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor(@Qualifier("validator") Validator validator) {
        MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
        processor.setValidator(validator);	// 若不定製validator,此處可不用set
        return processor;
    }

    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                .failFast(true)
                .buildValidatorFactory();
        return validatorFactory.getValidator();
    }

}

2.2 方法二 手動實例化validator

ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
    .configure()
    .failFast(true)
    .buildValidatorFactory();
Validator validator = validatorFactory.getValidator();

2.3 補充說明

  • failFast默認爲false,即會校驗徹底部參數後再返回所有參數的校驗結果信息
  • .failFast(true)也可用.addProperty("hibernate.validator.fail_fast", "true")替代,如果用的defaultProvider,即Validation.byDefaultProvider(),則只能使用後者

3、校驗接口方法入參

3.1 方法一

如果經過上面方法一實例化validator,則不須要手動校驗spring

3.2 方法二

如果使用的方法二,則可經過下面方法來校驗方法入參api

Set<ConstraintViolation<Object>> constraintViolationSet = validator.forExecutables().validateParameters(bean, method, args);
  • 入參bean是接口實現bean,非入參bean
  • 也可以使用validator.validate(arg)來遍歷校驗單個參數,但要求arg不能爲null,有些接口方法是平鋪入參,且部分入參可爲null的則不適應

4、獲取校驗失敗返回信息

能夠經過AOP或Filter來處理方法入參校驗tomcat

4.1 獲取校驗結果

  • 如果經過上面方法一,則直接捕捉javax.validation.ConstraintViolationException異常便可,而後e.getConstraintViolations()便可獲取到Set<ConstraintViolation>
  • 如果經過上面方法二,則校驗結果直接返回了Set<ConstraintViolation>

4.2 拼裝校驗信息

// constraintViolationSet = 上面校驗結果Set
if (constraintViolationSet.isEmpty()) {
    return null;
}
StringBuilder errorMsg = new StringBuilder();
for (ConstraintViolation violation : constraintViolationSet) {
    errorMsg.append(";");
    errorMsg.append(violation.getMessage());
}
return errorMsg.substring(1);

5、使用示例

5.1 入參類

import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

public class User {

    @NotBlank(message = "名稱不能爲空")
    private String name;

    @NotNull(message = "年齡不能爲空")
    @Min(value = 1, message = "年齡不能小於{value}")
    private Integer age;

    // .... getters and setters

}

5.2 接口方法

// 接口
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

public interface UserService {

    void register1(@NotNull(message = "請求入參不能爲空") @Valid User user);

    void register2(@NotBlank(message = "名稱不能爲空") String name, Integer age);

}
// 實現類
import org.springframework.validation.annotation.Validated;

@Validated
public class UserServiceImpl implements UserService {

    public void register1(User user) {
		// ....
	}

    public void register2(String name, Integer age) {
		// ....
	}

}
  • 若入參是個大參數,如register1的入參,則必須加@Valid註解,不然大參數裏的屬性不會校驗
  • 實現類上必須加@Validated註解

6、FAQ

6.1 若不指定校驗失敗的message,則會返回什麼

若不指定message,則會返回默認的message,即註解的message默認值,如@NotNull註解默認message佔位符是{javax.validation.constraints.NotNull.message},以下圖
根據佔位符可搜索到在hibernate-validator包下的Resource配置文件中,以下圖
裏面有對應的中文版本,以下圖
上圖上有各類語言版本,針對中文版的內容使用ASCII碼,可經過工具轉成native查看具體的message。app

6.2 報錯「Caused by: java.lang.NoClassDefFoundError: org/hibernate/validator/internal/engine/DefaultClockProvider」

緣由是包衝突,根本緣由是引入hibernate-validator依賴包後出現了validation-api的1.1.0.Final版本,應用中依賴的spring-boot中聲明瞭validation-api-1.1.0.Final包,因此在引入時出現了衝突。 解決:能夠顯示在parent的POM中顯示依賴validation-api-2.0.1.Final便可。ide

6.3 報錯「HV000183: Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead」

緣由是沒有依賴el相關包,增長以下包依賴便可spring-boot

<dependency>
    <groupId>javax.el</groupId>
    <artifactId>javax.el-api</artifactId>
    <version>3.0.1-b06</version>
</dependency>
<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>javax.el</artifactId>
    <version>2.2.6</version>
</dependency>

6.4 如何依賴一個包便可包含上面包的全部依賴

可依賴如下包,即包含全部依賴,如有依賴包衝突則一樣需單獨處理工具

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>

上面spring-boot-starter-validation包無其餘邏輯,僅單純依賴了hibernate-validator-6.0.13.Finaltomcat-embed-el-8.5.31spring-boot-starter-1.5.13.RELEASE3個包ui

相關文章
相關標籤/搜索