前言
我們在java項(xiàng)目開發(fā)中(典型場景為springmvc項(xiàng)目的controller參數(shù)接收)都需要對參數(shù)合法性(非空性,范圍,格式等)進(jìn)行校驗(yàn),最簡單的寫法是使用if...else進(jìn)行逐個判斷,但是此類開發(fā)工作繁瑣,代碼不夠優(yōu)雅,這里我們可以使用jsr-303中的一項(xiàng)子規(guī)范Bean Validation來處理,其中Hibernate Validator 是 Bean Validation 的參考實(shí)現(xiàn),本次演示便是對Hibernate Validator的使用和擴(kuò)展,千言萬語不如代碼一篇!
正文環(huán)境關(guān)鍵詞:idea、maven、springboot、hibernate-validator
一、代碼清單
Example.java? 自定義擴(kuò)展校驗(yàn)注解及Hibernate Validator標(biāo)準(zhǔn)注解使用示例類
InEnumValues.java? 自定義擴(kuò)展校驗(yàn)注解類
InEnumValuesValidatorImpl.java? 自定義擴(kuò)展校驗(yàn)實(shí)現(xiàn)類
ValidateUtil.java? 核心校驗(yàn)工具類
二、具體實(shí)現(xiàn)
package cn.seally.collector.validator;
import cn.seally.collector.common.ApiException;
import org.apache.commons.lang3.StringUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.*;
/**
* @Description 參數(shù)校驗(yàn)工具類
*
* 該類是基于 Hibernate Validator 構(gòu)建
* 而Hibernate Validator是 基于JSR-303標(biāo)準(zhǔn)的 validation bean 的實(shí)現(xiàn)之一
* Springboot默認(rèn)集成了Hibernate Validator,如果是非Springboot項(xiàng)目則需要pom主動添加以下依賴
*
* <dependency>
* <groupId>javax.validation</groupId>
* <artifactId>validation-api</artifactId>
* <version>2.0.1.Final</version>
* </dependency>
*
* 一、原始標(biāo)準(zhǔn)校驗(yàn)注解列表:
* AssertFalse,AssertTrue,DecimalMax,DecimalMin,Digits,Email,Future,FutureOrPresent,Max,Min,Negative,NegativeOrZero,NotBlank,NotEmpty,NotNull,Null,Past,PastOrPresent,Pattern,Positive,PositiveOrZero,Size
*
* 二、自定義擴(kuò)展校驗(yàn)注解列表:
* InEnumValues
*
* @Date 2021/11/30 00:17
* @Author dengningcheng
**/
public class ValidateUtil {
private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
/**
* @Description 對象屬性值校驗(yàn)方法
* @param t 需要驗(yàn)證的對象
* @param fields 需要驗(yàn)證的字段,如果不指定則校驗(yàn)所有注解字段,如果傳遞則只校驗(yàn)指定的字段
* @Date 2021/11/30 01:08
* @Author dengningcheng
**/
public static <T> String validate(T t,String... fields) throws ApiException {
if (null == t) {
return "校驗(yàn)對象為空";
}
List<String> errorList = new ArrayList<>();
Set<ConstraintViolation<T>> constraintViolations = new HashSet<>();
boolean checkALL = true;
if(null != fields && fields.length > 0){
for(String field: fields){
if(null != field && !field.trim().isEmpty()){
checkALL = false;
constraintViolations.addAll(validator.validateProperty(t,field));
}
}
}
if(checkALL){
constraintViolations = validator.validate(t);
}
for (ConstraintViolation<T> constraintViolation : constraintViolations) {
errorList.add("["+constraintViolation.getPropertyPath()+"]"+constraintViolation.getMessage());
}
if ((null != errorList) && !errorList.isEmpty()) {
return StringUtils.join(errorList, ";");
}
return null;
}
}
package cn.seally.collector.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
/**
* @Description 自定義校驗(yàn)注解:檢測屬性是否在列舉的枚舉值之列
* @Date 2021/12/1 00:25
* @Author dengningcheng
**/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = InEnumValuesValidatorImpl.class)//此處指定了注解的實(shí)現(xiàn)類為InEnumValuesValidatorImpl
public @interface InEnumValues {
/**
* @Description 1、自定義該校驗(yàn)注解需要的注解屬性,用于使用該注解到字段上時,傳入注解元信息,到時候會在注解校驗(yàn)實(shí)現(xiàn)類中用到,可以自定義類型和數(shù)量
* @Date 2021/12/1 00:52
* @Author dengningcheng
**/
int[] intEnums() default {};
String[] strEnums() default {};
/**
* @Description 2、以下為擴(kuò)展校驗(yàn)的標(biāo)準(zhǔn)屬性,擴(kuò)展時原樣寫,動態(tài)修改message的默認(rèn)提示語即可
* @Date 2021/12/1 00:52
* @Author dengningcheng
**/
String message() default "不在指定枚舉值范圍";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* @Description 3、定義List,為了讓Bean的一個屬性上可以添加多套規(guī)則,自定義時可照寫僅僅修改該注解的屬性類型如此處InEnumValues
* @Date 2021/12/1 00:52
* @Author dengningcheng
**/
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface List {
InEnumValues[] value();
}
}
package cn.seally.collector.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;
/**
* @Description 自定義校驗(yàn)處理最終處理類,必須要實(shí)現(xiàn)ConstraintValidator接口,范型為 <注解類,校驗(yàn)時傳入的檢測目標(biāo)值類>
* @Date 2021/12/1 00:21
* @Author dengningcheng
**/
public class InEnumValuesValidatorImpl implements ConstraintValidator<InEnumValues, Object> {
/**
* @Description 1、這里添加自己校驗(yàn)實(shí)現(xiàn)方法isValid中需要用到的一些輔助字段,可以時注解中的屬性,也可以是其他類型數(shù)據(jù),根據(jù)校驗(yàn)需要而定,這些屬性的初始化可以在initialize方法的入?yún)⒆⒔饽玫絹泶鎯蚴墙M裝(也就是拿到最終使用時注解到屬性上面的注解信息)
* @Date 2021/12/1 00:40
* @Author dengningcheng
**/
Set<String> values = new HashSet<>();//這里我本意是想要校驗(yàn)基礎(chǔ)常用類型比如整型和字符串行的枚舉值,但是整型和字符串型都可以轉(zhuǎn)為字符串統(tǒng)一判斷,因此定義一個字符串型的Set<String>來盛裝注解中的intEnums和strEnums中獲取到的校驗(yàn)比對范圍
/**
* @Description 2、重寫該方法,校驗(yàn)時會傳遞進(jìn)來校驗(yàn)字段的注解,通過注解獲取到我們校驗(yàn)時字段注解上的一些元信息,我們用此元信息來初始化幫助我們在isValid進(jìn)行比對判斷的values
* @Date 2021/12/1 00:39
* @Author dengningcheng
**/
@Override
public void initialize(InEnumValues constraintAnnotation) {
if(null != constraintAnnotation.intEnums()){
for(int i : constraintAnnotation.intEnums()){
values.add(i+"");
}
}
if(null != constraintAnnotation.strEnums()){
for(String i : constraintAnnotation.strEnums()){
values.add(i);
}
}
}
/**
* @Description 3、重寫該方法,實(shí)現(xiàn)自己真正的校驗(yàn)邏輯
* @Date 2021/12/1 00:42
* @Author dengningcheng
**/
@Override
public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) {
//如果校驗(yàn)值是null認(rèn)為校驗(yàn)通過,因?yàn)橛袑iT的非null校驗(yàn)注解去做非null的校驗(yàn)
if(null == object){
return true;
}
//如果該注解包含需要校驗(yàn)的
if(null != values && !values.isEmpty()){
return values.contains(object.toString());
}
return true;
}
}
package cn.seally.collector.validator;
import javax.validation.constraints.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
/**
* @Description 參數(shù)校驗(yàn)使用示例類,具體使用參照該類的main方法,可用注解查閱ValidateUtil類的說明
* @Date 2021/11/30 01:16
* @Author dengningcheng
**/
public class Example {
@NotNull
private String name;
@NotEmpty
private String gender;
@AssertTrue
@NotNull
private Boolean woman;
@Pattern(regexp="^1\d{10}$")
@NotNull
private String telephone;
@Min(1)
@Max(100)
@NotNull
private Integer age;
@Size(min = 1,max = 2)
private String address;
@Digits(integer = 3,fraction = 2)
@NotNull
private Double weight;
private Long foot;
private Date birth;
@DecimalMin("0.00")
@DecimalMax("2.00")
@NotNull
private BigDecimal money;
private Float salary;
@InEnumValues(strEnums = {"ellie","dennis"})
@NotNull
private String friend;
@InEnumValues(intEnums = {1,2,3})
private Integer level;
@NotEmpty
@Size(min=1,max = 2,message = "個數(shù)超出期望值范圍")
private List<String> hobbies;
private Set<Integer> friends;
public static void main(String[] args) {
Example param = new Example();
param.setWoman(false);
param.setAddress("你好么");
param.setMoney(new BigDecimal("-12.0"));
ArrayList<String> hobbies = new ArrayList<>();hobbies.add("打球");hobbies.add("看書");hobbies.add("看電影");
param.setHobbies(hobbies);
param.setWeight(Double.valueOf(123.234D));
param.setTelephone("130");
param.setFriend("dnc");
param.setLevel(4);
System.out.println("param校驗(yàn)所有屬性結(jié)果為: "+ValidateUtil.validate(param));
System.out.println("param校驗(yàn)指定屬性結(jié)果為: "+ValidateUtil.validate(param,"name"));
}
public String getFriend() {
return friend;
}
public void setFriend(String friend) {
this.friend = friend;
}
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Boolean getWoman() {
return woman;
}
public void setWoman(Boolean woman) {
this.woman = woman;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getWeight() {
return weight;
}
public void setWeight(Double weight) {
this.weight = weight;
}
public Long getFoot() {
return foot;
}
public void setFoot(Long foot) {
this.foot = foot;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public BigDecimal getMoney() {
return money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
public Float getSalary() {
return salary;
}
public void setSalary(Float salary) {
this.salary = salary;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public Set<Integer> getFriends() {
return friends;
}
public void setFriends(Set<Integer> friends) {
this.friends = friends;
}
}
本文摘自 :https://blog.51cto.com/u