Bean Validation 將約束拆解為兩部分:約束註解(聲明語義)與約束驗證器(執行邏輯)。每個註解必須綁定一個實現 ConstraintValidator<A, T> 的驗證器類。
| 註解 | 語義 |
|---|---|
@Null / @NotNull | 空 / 非空檢查 |
@AssertTrue / @AssertFalse | Boolean 必須為 true/false |
@Min / @Max / @DecimalMin / @DecimalMax | 數值/字串範圍邊界(支援精度) |
@Size | 集合、陣列、Map、字串長度範圍 |
@Digits | 數字/字串格式合法性 |
@Past / @Future | 日期/Calendar 必須在當前時間之前/之後 |
@Pattern | 字串符合正則表達式 |
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { NotEmptyValidator.class })
public @interface NotEmpty {
String message() default "this string may be empty";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
@Target:聲明作用位置(方法、欄位、類型、建構子、參數等)。@Retention(RUNTIME):運行期保留,供反射讀取。@Constraint:綁定驗證器實現類。message:違規預設訊息,支援插值表達式。groups:驗證分組,支援多階段驗證流程。payload:攜帶元數據(如嚴重等級:ERROR/WARN/INFO)。public interface ConstraintValidator<A extends Annotation, T> {
void initialize(A constraintAnnotation); // 初始化,讀取註解屬性
boolean isValid(T value, ConstraintValidatorContext context); // 核心驗證邏輯
}
// 註解定義(見上)
public class NotEmptyValidator implements ConstraintValidator<NotEmpty, String> {
public void initialize(NotEmpty params) { /* 可讀取註解屬性 */ }
public boolean isValid(String str, ConstraintValidatorContext ctx) {
return str != null && str.length() > 0;
}
}
在註解類內部定義巢狀 @interface List 並宣告 ConstraintName[] value() 陣列,即可在使用時以 @ConstraintName.List({ @ConstraintName(...), @ConstraintName(...) }) 形式疊加多條同類型約束,全部通過才算驗證通過。
@PatternOfString.List({
@PatternOfString(mustContainLetter = "CH", message = "Not in China"),
@PatternOfString(mustContainLetter = "MainLand", message = "Not in Mainland")
})
private String place;
若 place = "C",兩條違規訊息均輸出;若 place = "CHINA",僅輸出第二條違規。
直接在新註解上疊加現有註解,即可語義組合。例如 @NotEmpty2 等價於 @NotNull + @Size(min=1):
@NotNull
@Size(min = 1)
@Target({...})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { NotEmptyValidator2.class })
public @interface NotEmpty2 { ... }
使用 @NotEmpty2 即獲得雙重保護,無需重寫驗證器邏輯(驗證器可留空或委派)。
完整範例運行輸出展示了違規訊息的聚合與順序,驗證流程嚴格遵循聲明順序與分組規則。