學習Validator驗證框架總結


在項目開發中許多地方需要加以驗證,對於使用if-else簡單粗暴一個一個驗證,spring的validation封裝了Javax ValidationI校驗參數,大大縮減了代碼量。

以前的分層驗證,從controller到落入數據庫,一層一層驗證,代碼重復、冗余。

Javax ValidationI使用Java Bean驗證通過注解將約束添加到域模型中,將驗證邏輯從代碼中分離出來。

Javax ValidationI的依賴:

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.16.Final</version>
 </dependency>

  

springboot對Javax ValidationI封裝,依賴變成

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

 一、初級注解

JSR提供的驗證注解:

@Null   被注釋的元素必須為 null    

@NotNull    被注釋的元素必須不為 null    

@AssertTrue     被注釋的元素必須為 true    

@AssertFalse    被注釋的元素必須為 false    

@Min(value)     被注釋的元素必須是一個數字,其值必須大於等於指定的最小值   &nbsp

@Max(value)     被注釋的元素必須是一個數字,其值必須小於等於指定的最大值    

@DecimalMin(value)  被注釋的元素必須是一個數字,其值必須大於等於指定的最小值    

@DecimalMax(value)  被注釋的元素必須是一個數字,其值必須小於等於指定的最大值    

@Size(max=, min=)   被注釋的元素的大小必須在指定的范圍內    

@Digits (integer, fraction)     被注釋的元素必須是一個數字,其值必須在可接受的范圍內    

@Past   被注釋的元素必須是一個過去的日期    

@Future     被注釋的元素必須是一個將來的日期    

@Pattern(regex=,flag=)  被注釋的元素必須符合指定的正則表達式

Validator提供的驗證注解:

@NotBlank(message =)   驗證字符串非null,且長度必須大於0    

@Email  被注釋的元素必須是電子郵箱地址    

@Length(min=,max=)  被注釋的字符串的大小必須在指定的范圍內    

@NotEmpty   被注釋的字符串的必須非空    

@Range(min=,max=,message=)  被注釋的元素必須在合適的范圍內

二、創建驗證器

/**
 * 驗證測試類
 */
public class ValidationTest {

    // 驗證器對象
    private Validator validator;
    // 待驗證對象
    private UserInfo userInfo;
    // 驗證結果集合
    private Set<ConstraintViolation<UserInfo>> set;

    /**
     * 初始化操作
     */
    @Before
    public void init() {
        // 初始化驗證器
        validator = Validation.buildDefaultValidatorFactory()
                .getValidator();

        // 初始化待驗證對象 - 用戶信息
        userInfo = new UserInfo();

    }

    /**
     * 結果打印
     */
    @After
    public void print() {
        set.forEach(item -> {
            // 輸出驗證錯誤信息
            System.out.println(item.getMessage());
        });

    }

    @Test
    public void nullValidation() {
        // 使用驗證器對對象進行驗證
        set = validator.validate(userInfo);
    }

}

  三、中級約束

    中級約束中分為分組約束、組序列

public class UserInfo {

    // 登錄場景
    public interface LoginGroup {}

    // 注冊場景
    public interface RegisterGroup {}

    /**
     * 用戶ID
     */
    @NotNull(message = "用戶ID不能為空",
            groups = LoginGroup.class)
    private String userId;

    /**
     * 用戶名
     * NotEmpty 不會自動去掉前后空格
     */
    @NotEmpty(message = "用戶名稱不能為空",groups = RegisterGroup.class)
    private String userName;

    /**
     * 用戶密碼
     * NotBlank 自動去掉字符串前后空格后驗證是否為空
     */
    @NotBlank(message = "用戶密碼不能為空")
    @Length(min = 6, max = 20,
            message = "密碼長度不能少於6位,多於20位")
    private String passWord;

}

  

  /**
     * 分組驗證測試方法
     */
    @Test
    public void groupValidation() {
        set = validator.validate(userInfo,
                UserInfo.RegisterGroup.class,
                UserInfo.LoginGroup.class);
    }

  組排序:

public class UserInfo {

    // 登錄場景
    public interface LoginGroup {}

    // 注冊場景
    public interface RegisterGroup {}

    // 組排序場景
    @GroupSequence({
            LoginGroup.class,
            RegisterGroup.class,
            Default.class
    })
    public interface Group {}

    /**
     * 用戶ID
     */
    @NotNull(message = "用戶ID不能為空",
            groups = LoginGroup.class)
    private String userId;

    /**
     * 用戶名
     * NotEmpty 不會自動去掉前后空格
     */
    @NotEmpty(message = "用戶名稱不能為空", 
            groups = RegisterGroup .class)
    private String userName;

    /**
     * 用戶密碼
     * NotBlank 自動去掉字符串前后空格后驗證是否為空
     */
    @NotBlank(message = "用戶密碼不能為空")
    @Length(min = 6, max = 20,
            message = "密碼長度不能少於6位,多於20位")
    private String passWord;
}    

  

    /**
     * 組序列測試
     */
    @Test
    public void groupSequenceValidation() {
        set = validator.validate(userInfo,
                UserInfo.Group.class);
    }

  三、高級約束

    高級約束是對參數、返回值的約束。

    使用注解:

      javax:@Valid

      spring:@Validated

    在檢驗參數、返回值是否符合規范時,使用@Validated或者@Valid在基本驗證功能上沒有太多區別。但是在分組、注解地方、嵌套驗證等功能上兩個有所不同。

    @Validated:提供了一個分組功能,可以在入參、返回值驗證時,根據不同的分組采用不同的驗證機制。

    @Valid:不支持分組功能。

    注解地方:

    @Validated:可以用在類型、方法和方法參數上。但是不能用在成員屬性(字段)上。

    @Valid:可以用在方法、構造函數、方法參數和成員屬性(字段)上。

/**
 * 用戶信息
 */
public class UserInfoService {

    /**
     * UserInfo 作為輸入參數
     * @param userInfo
     */
    public void setUserInfo(@Valid UserInfo userInfo) {}

    /**
     * UserInfo 作為輸出參數
     * @return
     */
    public @Valid UserInfo getUserInfo() {
        return new UserInfo();
    }

}

  

/**
 * 驗證測試類
 */
public class ValidationTest {

    // 驗證器對象
    private Validator validator;
    // 待驗證對象
    private UserInfo userInfo;
    // 驗證結果集合
    private Set<ConstraintViolation<UserInfoService>> otherSet;

    /**
     * 初始化操作
     */
    @Before
    public void init() {
        // 初始化驗證器
        validator = Validation.buildDefaultValidatorFactory()
                .getValidator();

        // 初始化待驗證對象 - 用戶信息
        userInfo = new UserInfo();
    }

    /**
     * 結果打印
     */
    @After
    public void print() {
        set.forEach(item -> {
            // 輸出驗證錯誤信息
            System.out.println(item.getMessage());
        });

    }


    /**
     * 對方法輸入參數進行約束注解校驗
     */
    @Test
    public void paramValidation() throws NoSuchMethodException {
        // 獲取校驗執行器
        ExecutableValidator executableValidator =
                validator.forExecutables();

        // 待驗證對象
        UserInfoService service = new UserInfoService();
        // 待驗證方法
        Method method = service.getClass()
                .getMethod("setUserInfo", UserInfo.class);
        // 方法輸入參數
        Object[] paramObjects = new Object[]{new UserInfo()};

        // 對方法的輸入參數進行校驗
        otherSet = executableValidator.validateParameters(
                service,
                method,
                paramObjects);
    }


    /**
     * 對方法返回值進行約束校驗
     */
    @Test
    public void returnValueValidation()
            throws NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {

        // 獲取校驗執行器
        ExecutableValidator executableValidator =
                validator.forExecutables();

        // 構造要驗證的方法對象
        UserInfoService service = new UserInfoService();
        Method method = service.getClass()
                .getMethod("getUserInfo");

        // 調用方法得到返回值
        Object returnValue = method.invoke(service);

        // 校驗方法返回值是否符合約束
        otherSet = executableValidator.validateReturnValue(
                service,
                method,
                returnValue);
    }

}

  在controller中驗證入參一般使用@Validated

@RequestMapping(method = RequestMethod.POST)
    public UserInfo  create(@RequestBody @Validated( { RegisterGroup.class }) UserInfo  userInfo) {
		return userService.create(userInfo);
    }

  @RequestMapping(method = RequestMethod.GET)
    public UserInfo  getUserById(@NotNull(message = "id不能為空")  int userId)  {
       return userService.getUserById(userId);
    }

  四、自定義約束注解

/**
 * 自定義手機號約束注解
 */
@Documented
// 注解的作用目標
@Target({ElementType.FIELD})
// 注解的保留策略
@Retention(RetentionPolicy.RUNTIME)
// 不同之處:於約束注解關聯的驗證器
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {

    // 約束注解驗證時的輸出信息
    String message() default "手機號校驗錯誤";

    // 約束注解在驗證時所屬的組別
    Class<?>[] groups() default {};

    // 約束注解的有效負載(嚴重程度)
    Class<? extends Payload>[] payload() default {};
}

  

/**
 * 自定義手機號約束注解關聯驗證器
 */
public class PhoneValidator
        implements ConstraintValidator<Phone, String> {

    /**
     * 自定義校驗邏輯方法
     * @param value
     * @param context
     * @return
     */
    @Override
    public boolean isValid(String value,
                           ConstraintValidatorContext context) {

        // 手機號驗證規則:158后頭隨便
        String check = "158\\d{8}";
        Pattern regex = Pattern.compile(check);

        // 空值處理
        String phone = Optional.ofNullable(value).orElse("");
        Matcher matcher = regex.matcher(phone);

        return matcher.matches();
    }
}

  自定義注解使用:

public class UserInfo{
    /**
     * 手機號
     */
    @Phone(message = "手機號不是158后頭隨便")
    private String phone;
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM