前言
通常我們在開發的過程中,需要對前端傳入的資料進行校驗,盡管這一步已經在前端進行了一次校驗,雖然現在已經有了很多校驗的注解,@NotNull、@NotBlank、@URL等一系列注解幫助我們進行校驗,但是在實際的業務開發過程中,這些可能不足以滿足我們的需求,這時候我們就需要自己來定義注解了。
可能需要用到的依賴按需導入:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.3.0.Final</version>
</dependency>
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>classmate</artifactId>
<version>1.3.3</version>
</dependency>
@Constraint
這個注解的主要作用就是幫助我們來處理驗證邏輯的,根據根據自己的業務需求來完成這一塊驗證的邏輯。下面我們就來簡單測試一下。
**場景 **:假設我們一個實體類的屬性如下解釋,我們需要對前端傳入的數字是不是0或1。
/**
* 顯示狀态[0-不顯示;1-顯示]
*/
private Integer showStatus;
定義注解
@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class})
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.CONSTRUCTOR,ElementType.PARAMETER,ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ListValue {
String message() default "{com.atguigu.common.valid.ListValue.message}";
Class<?>[] groups() default {};
int[] vals() default {};
Class<? extends Payload>[] payload() default {};
}
message():錯誤資訊,如果我們沒有在注解中定義錯誤資訊的話,他會預設去尋找com.atguigu.common.valid.ListValue.message為key的錯誤資訊
groups():這個主要是來進行分組驗證的。
** vals() ** :自行定義的值
我們可以看到@Constraint中
@Constraint(validatedBy = {ListValueConstraintValidator.class})
這段意思是我們将此注解的驗證邏輯交給ListValueConstraintValidator來處理,這裡是一個數組,我們可以傳入多個處理邏輯。
ConstraintValidator接口,它有兩個泛型,第一個是自定義的注解類,第二個就是要驗證的資料的類型,這兩個類裡面都有兩個方法,initialize和isValid,第一個是初始化方法,第二個是驗證的邏輯方法,傳回true,則驗證通過,否則則不通過。
ListValueConstraintValidator自定義驗證邏輯
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
private Set<Integer> set = new HashSet<>();
/**
* 初始化 加載注解的資訊。也就是帶有此注解Bean上的vals值
* @param constraintAnnotation
*/
@Override
public void initialize(ListValue constraintAnnotation) {
int[] vals = constraintAnnotation.vals();
for (int val : vals) {
set.add(val);
}
}
/**
* 判斷是否校驗成功
* @param value 需要校驗的值 實際傳入的值
* @param context 上下文資訊
* @return
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
** ValidationMessages.properties **
規範(JSR303)說,我必須将ValidationMessages.properties放到我的類路徑根目錄中,我希望它應該是MyDeploymentUnit.war / WEB-INF / classes / ValidationMessages.properties,這就是我的應用程式的建構和部署方式。出于調試原因,我将此代碼添加到了自定義驗證器中,以確定檔案存在并正确設定。
key就是我們在注解定義的預設 message的值。
測試
在Controller中需要進行校驗的Bean加上 ** @Validated ** 注解
需要驗證的 屬性
這裡的如果校驗不通過的話,他會抛出一個異常,我們隻需要捕獲到這個錯誤資訊即可。
如果我們去掉屬性聲明的message資訊 他會去尋找預設的ValidationMessages.properties下的資訊。
分組校驗
可能大家會注意到我在Controller中使用 @Validated 注解時會帶有一個value值,他的作用是對操作進行分組。比如請看如下
@NotNull(message = "修改商品時請傳入商品id",groups = {UpdateGroup.class})
@Null(groups = {AddGroup.class},message = "不能指定id")
@TableId
private Long brandId;
商品的資料表的對應的ID,我們可能在進行新增操作時無需我們來指定對應的ID,因為我們一般設定都會預設自增的。而我們在查詢或者更新的時候可能會根據ID來查出這一條記錄。總的來說的就是在進行更新時需要ID,新增不需要。每一個注解都會帶有groups這個屬性來聲明自己的組。
如何使用呢?
很簡單:
定義對應的接口或者類。
public interface AddGroup {
}
// ------------------------------
public interface UpdateGroup {
}
//---------------------------------
public interface UpdateStatusGroup {
}
我們隻需要定義三個接口來聲明組,無需任何操作。
在Controller中的@Validated 注解中聲明對那個組進行驗證。
@RequestMapping("/save")
public R save(@Validated(value = {AddGroup.class}) @RequestBody BrandEntity brand) {
brandService.save(brand);
return R.ok();
}
實體類進行分組邏輯
@NotNull(message = "修改商品時請傳入商品id",groups = {UpdateGroup.class})
@Null(groups = {AddGroup.class},message = "不能指定id")
@TableId
private Long brandId;
驗證: 新增時:傳入ID會失敗
更新時:不傳ID 會失敗