前言
通常我們在開發的過程中,需要對前端傳入的數據進行校驗,儘管這一步已經在前端進行了一次校驗,雖然現在已經有了很多校驗的註解,@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 會失敗