javax.validation包里@NotNull等注解的使用方式
在做項目的時候,對pojo和傳入的參數(shù)進行校驗,如果是代碼編寫,需要很多if來判斷
其實可根據(jù)一些校驗的注解來實現(xiàn)我們的參數(shù)校驗,主要介紹一下常用的 javax.validation 這個倉庫的使用,這里總結(jié)一下
1、導(dǎo)包
在項目的pom.xml 文件夾中導(dǎo)入包
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api --> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency>
這個驗證根據(jù)JSR 380規(guī)范,validation-api 依賴包含標(biāo)準(zhǔn)驗證注解
2、使用
這個包的注解主要有以下這么多,先簡單說明(以版本 號2.0.2為標(biāo)準(zhǔn))
下面再分別介紹
注解名稱 | 驗證得類型 | 描述 |
---|---|---|
AssertFalse | Boolean,boolean | 被注解的元素屬性值必須為false |
AssertTrue | Boolean,boolean | 被注解的元素屬性值必須為true |
DecimalMax | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存儲的是數(shù)字)子類型 | 驗證注解的元素值小于等于指定的value值 |
DecimalMin | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存儲的是數(shù)字)子類型 | 驗證注解的元素值大于等于指定的value值 |
Digits | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存儲的是數(shù)字)子類型 | 驗證注解的元素值的整數(shù)位數(shù)和小數(shù)位數(shù)上限 |
Max | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存儲的是數(shù)字)子類型 | 被注解的元素值的類型必須為數(shù)字,其值必須小于等于指定的最大值 |
Min | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存儲的是數(shù)字)子類型 | 被注解的元素值的類型必須為數(shù)字,其值必須大于等于指定的最小值 |
CharSequence子類型(如String) | ||
Future | java.util.Date,java.util.Calendar;Joda Time類庫的日期類型 | 被注解的元素值得范圍必須為未來的一個時間 |
FutureOrPresent | java.util.Date,java.util.Calendar;Joda Time類庫的日期類型 | 被注解的元素值得范圍必須為未來的一個時間或者現(xiàn)在得時間 |
Past | java.util.Date,java.util.Calendar;Joda Time類庫的日期類型 | 被注解的元素的值范圍必須為過去的一個時間 |
PastOrPresent | java.util.Date,java.util.Calendar;Joda Time類庫的日期類型 | 被注解的元素的值范圍必須為過去或者現(xiàn)在的一個時間 |
Negative | 數(shù)值 | 適用于數(shù)值并驗證它們是嚴(yán)格負數(shù),不包括0 |
NegativeOrZero | 數(shù)值 | 適用于數(shù)值并驗證它們是嚴(yán)格負數(shù),包括0 |
NotBlank | CharSequence子類型 | 驗證注解的元素值不為空(不為null、去除首位空格后長度為0),不同于@NotEmpty,@NotBlank只應(yīng)用于字符串且在比較時會去除字符串的首位空格 |
NotEmpty | CharSequence子類型、Collection、Map、數(shù)組 | 驗證注解的元素值不為null且不為空(字符串長度不為0、集合大小不為0) |
NotNull | 任意類型 | 被注解的元素屬性值必須非null |
Null | 任意類型 | 被注解的元素屬性值必須為null |
Pattern | String,任何CharSequence的子類型 | 被注解的元素值必須符合指定的正則表達式 |
Positive | 數(shù)值 | 適用于數(shù)值并驗證它們是嚴(yán)格正數(shù),不包括0 |
PositiveOrZero | 數(shù)值 | 適用于數(shù)值并驗證它們是嚴(yán)格正數(shù),包括0 |
Size | String, Collection, Map和數(shù)組屬性 | 被注解的元素屬性值的大小必須在指定范圍內(nèi) |
2.0 注解的介紹
// 表明注解可以使用在哪里 // 這里表示注解可以使用在 方法 屬性 注解 構(gòu)造方法 參數(shù) type @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) //描述保留注解的各種策略,它們與元注解(@Retention)一起指定注釋要保留多長時間 @Retention(RetentionPolicy.RUNTIME) //表明這個注解是由 javadoc記錄的 @Documented public @interface MyOwnAnnotation { }
2.1 注解詳細屬性的介紹
查看上面所有注解的源碼,關(guān)于注解中的字段,主要分為以下兩種
2.1.1 不能自定義值的
以下面的這些注解為代表
- AssertFalse AssertTrue
- PositiveOrZero Positive Negative NegativeOrZero
- NotNull Null NotEmpty NotBlank
- FutureOrPresent Future PastOrPresent Past
以 @AssertFalse為例,該注解的詳細內(nèi)容(源碼)
@Documented @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) // 這個表明可以重復(fù)使用, //例如在一個field使用這個注解 // 一般注解只能使用一次 //@Max(value = 11) //Long num; // 如果使用了這個@Repeatable標(biāo)注,那就可以使用兩次 //@Max(value = 11) //@Max(value = 22) //Long num; @Repeatable(AssertFalse.List.class) // 這個Constraint 指定驗證類,如果不自定義指定,實現(xiàn)其默認(rèn)的驗證類 ,如果指定 // @Constraint(validatedBy = { MyValidator.class }) @Constraint(validatedBy = {}) public @interface AssertFalse { // 校驗失敗的信息,可以自定義 String message() default "{javax.validation.constraints.AssertFalse.message}"; // 這里主要進行將validator進行分類,不同的類group中會執(zhí)行不同的validator操作 Class<?>[] groups() default {}; // 這個主要是指定bean,一般很少使用 Class<? extends Payload>[] payload() default {}; @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface List { AssertFalse[] value(); } }
2.1.2 能自定義值的
其他的注解都是可以自定義一些值,作為校驗的參照值
以 @Max為例
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Repeatable(Max.List.class) @Documented @Constraint(validatedBy = {}) public @interface Max { // 校驗失敗的信息,可以自定義 String message() default "{javax.validation.constraints.Max.message}"; // 這里主要進行將validator進行分類,不同的類group中會執(zhí)行不同的validator操作 Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; // 與上面不同的就是多了個value,這個需要自定義參數(shù)傳入的值與這個value值對比,也就是校驗的參考值 long value(); @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface List { Max[] value(); } }
2.1.3 groups屬性的使用
這個屬性的作用就是舉個例子:例如我們在傳參的時候,加入我們創(chuàng)建一個用戶,那么我們再傳入的參數(shù)是不會包含用戶的id,而如果更新這個用戶,我們就需要傳入用戶id,那么根據(jù)多種校驗規(guī)則的時候,這個屬性就起作用了。
如果我們在一個屬性使用注解的時候,如果不指定groups的時候,其實默認(rèn)是在Default 組別中
@NotNull(message = "姓名不能為空") private String name; 等同于 import javax.validation.groups.Default; @NotNull(message = "姓名不能為空" , groups = {Default.class}) private String name;
那么根據(jù)這個情況,如果我們想自定義組別的時候,我們就可以分為以下幾個步驟:
- 定義組別
- 使用組別
(1)定義組別
還是這個例子:如果創(chuàng)建一個用戶,不用傳入用戶的id,如果更新這個用戶,我們就需要傳入用戶id
那么我們就定義兩個組別,創(chuàng)建用戶組別、更新用戶組別(其實就是兩個接口,里面不用有方法,但是要注意要繼承Default接口,理由下面會說)
import javax.validation.groups.Default; public interface CreateUser extends Default { } import javax.validation.groups.Default; public interface UpdateUser extends Default{ }
(2)使用組別
1)首先在pojo中,我們需要使用組別,若屬性在不同的組別有不同的校驗方式,那么就特殊指定其需要校驗的規(guī)則,如果不指定,就還是默認(rèn)按照Default
這里需要注意:定義的組別最好要繼承Default接口,不然當(dāng)我們在指定規(guī)則的校驗時候,那么不標(biāo)注groups的屬性就不再校驗了,因為默認(rèn)按照Default
@Data class User{ @NotNull(message = "用戶id不能為空", groups = UpdateUser.class) private Integer id; @NotNull(message = "姓名不能為空" ,groups = {CreateUser.class, UpdateUser.class}) private String name; @NotNull(message = "性別不能為空") private String sex; @Max(value = 20 ,message = "最大長度為20") private String address; @Email(message = "不滿足郵箱格式") private String email; @AssertTrue(message = "字段為true才能通過") private boolean isAuth; @NotBlank(message = "手機號不能為空") @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手機號格式有誤") private String mobile; @Future(message = "時間在當(dāng)前時間之后才可以通過") private Date date; }
2)在接口中使用特定的組別。
兩個接:
- 創(chuàng)建user的時候,指定驗證的組別是CreateUser
- 更新user的時候,指定驗證的組別是UpdateUser
(當(dāng)自定義的組別繼承了Default,這里指定驗證組別的,屬性會根據(jù)指定的groups來進行校驗,那么沒有指定groups的都會校驗,如果不繼承Default,那沒有自定義groups為這個組別的屬性就不生效了)
/** * 走參數(shù)校驗注解的 groups 組合校驗 * * @param user * @return */ @PostMapping("/users/update") public ResponseDTO updateUser(@RequestBody @Validated(UpdateUser.class) User user) { userService.updateById(userDTO); return ResponseDTO.success(); } /** * 走參數(shù)校驗注解的 groups 組合校驗 * * @param user * @return */ @PostMapping("/users/save") public ResponseDTO saveUser(@RequestBody @Validated(CreateUser.class) User user) { userService.saveUser(userDTO); return ResponseDTO.success(); }
3、使用
3.1 第一種方式,pojo作為傳參的形式
- Pojo 定義驗證規(guī)則以
- pojo作為參數(shù)傳參
(1)User pojo類 定義校驗規(guī)則
@Data class User{ @NotNull(message = "姓名不能為空") private String name; @NotNull(message = "性別不能為空") private String sex; @Max(value = 20 ,message = "最大長度為20") private String address; @Email(message = "不滿足郵箱格式") private String email; @AssertTrue(message = "字段為true才能通過") private boolean isAuth; @NotBlank(message = "手機號不能為空") @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手機號格式有誤") private String mobile; @Future(message = "時間在當(dāng)前時間之后才可以通過") private Date date; }
(2)讓校驗規(guī)則生效。在Controller類的時候,我們只需要利用 @Validated 注解來實現(xiàn)pojo的校驗
@RequestMapping("users") public ResponseDTO saveUser( @RequestBody @Validated User user){ }
(3)捕捉驗證異常。如果參數(shù)校驗通過,那么就直接執(zhí)行接口方法,但是如果失敗了,我們?nèi)绾芜M行處理
MethodArgumentNotValidException
是springBoot中進行綁定參數(shù)校驗時的異常,需要在springBoot中處理
其他需要處理 ConstraintViolationException異常進行處理
一般像異常捕捉的,可以自定義一個異常捕捉Handler,實現(xiàn)異常捕捉后的數(shù)據(jù)返回
import com.dto.ResponseDTO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DuplicateKeyException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.NoHandlerFoundException; import javax.validation.ConstraintViolationException; import javax.validation.ValidationException; @RestControllerAdvice public class GlobalExceptionHandler { private Logger logger = LoggerFactory.getLogger(getClass()); private static int DUPLICATE_KEY_CODE = 1001; private static int PARAM_FAIL_CODE = 1002; private static int VALIDATION_CODE = 1003; /** * 處理自定義異常 */ @ExceptionHandler(BizException.class) public ResponseDTO handleRRException(BizException e) { logger.error(e.getMessage(), e); return new ResponseDTO(e.getCode(), e.getMessage()); } /** * 方法參數(shù)校驗 */ @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseDTO handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { logger.error(e.getMessage(), e); return new ResponseDTO(PARAM_FAIL_CODE, e.getBindingResult().getFieldError().getDefaultMessage()); } /** * ValidationException */ @ExceptionHandler(ValidationException.class) public ResponseDTO handleValidationException(ValidationException e) { logger.error(e.getMessage(), e); return new ResponseDTO(VALIDATION_CODE, e.getCause().getMessage()); } /** * ConstraintViolationException */ @ExceptionHandler(ConstraintViolationException.class) public ResponseDTO handleConstraintViolationException(ConstraintViolationException e) { logger.error(e.getMessage(), e); return new ResponseDTO(PARAM_FAIL_CODE, e.getMessage()); } /** * 路徑異常 */ @ExceptionHandler(NoHandlerFoundException.class) public ResponseDTO handlerNoFoundException(Exception e) { logger.error(e.getMessage(), e); return new ResponseDTO(404, "不好意思,路徑不存在,請檢查路徑是否正確"); } /** * 所有其他異常捕捉 */ @ExceptionHandler(Exception.class) public ResponseDTO handleException(Exception e) { logger.error(e.getMessage(), e); return new ResponseDTO(500, "不好意思,系統(tǒng)繁忙,請稍后再試"); } }
3.2 第二種方式,restful風(fēng)格
- Controller 上加上@Validated
- 參數(shù)上加注解
@RestController @RequestMapping("user/") @Validated public class UserController{ @RequestMapping("users) public ResponseDTO getUser(@RequestParam("userId") @NotNull(message = "用戶id不能為空") Long userId){ } }
4、自定義使用
如果我們想自定義一個驗證的注解,那么需要怎么做呢?
- 定義一個注解
- 編寫一個驗證類
- 使用
**(1)**我們首先定義一個像上面的@Null 這樣的注解
@Documented // 這里標(biāo)注該注解是使用在filed和參數(shù)上的 @Target({ElementType.PARAMETER, ElementType.FIELD}) // 運行時生效 @Retention(RetentionPolicy.RUNTIME) // 指定驗證的類是哪個 MyValidator 就是第二步做的事情 @Constraint(validatedBy = MyValidator.class) public @interface MyValid { // 提供自定義異常的信息,可以指定默認(rèn)值 String message() default "參數(shù)不合法"; // 分組,詳細看 上面的介紹 Class<?>[] groups() default {}; // 針對bean的,使用不多 Class<? extends Payload>[] payload() default {}; }
(2)編寫一個驗證類
我們需要編寫一個 實現(xiàn) ConstraintValidator 類實現(xiàn)類,來指定我們的校驗規(guī)則
如果不指定特定注解的情況下,直接使用
// 這個是Max的指定的驗證規(guī)則源碼 public abstract class AbstractMaxValidator<T> implements ConstraintValidator<Max, T> { protected long maxValue; public AbstractMaxValidator() { } public void initialize(Max maxValue) { this.maxValue = maxValue.value(); } public boolean isValid(T value, ConstraintValidatorContext constraintValidatorContext) { if (value == null) { return true; } else { return this.compare(value) <= 0; } } protected abstract int compare(T var1); } // 自定義的驗證類 public class MyValidator implements ConstraintValidator { @Override public void initialize(Annotation constraintAnnotation) { // 可以獲取注解的值 ,一般寫在該方法中 } @Override public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) { // 編寫自己屬性驗證的規(guī)則,o 則是待驗證的值 return false; } }
如果在指定特定注解的情況下,那么我們就可特定 注解
// 自定義的驗證類 public class MyValidator implements ConstraintValidator<MyValid , Object> { @Override public void initialize(MyValid myValid) { // 可以獲取注解的值 ,一般寫在該方法中 } @Override public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) { // 編寫自己屬性驗證的規(guī)則,o 則是待驗證的值 return false; } }
(3)使用。就跟正常的那些注解一樣使用即可。
@Data public Class Example{ @NotBlank(message = "姓名不能為空") @MyValid(message = "姓名有誤,請核對后提交") private String name; }
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Springboot整合多數(shù)據(jù)源代碼示例詳解
這篇文章主要介紹了Springboot整合多數(shù)據(jù)源代碼示例詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08springboot結(jié)合ehcache防止惡意刷新請求的實現(xiàn)
這篇文章主要介紹了springboot結(jié)合ehcache防止惡意刷新請求的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12通過Java實現(xiàn)中文分詞與文本關(guān)鍵詞提取
這篇文章主要為大家詳細介紹了如何利用Java實現(xiàn)中文分詞以及文本關(guān)鍵詞提取功能,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)學(xué)習(xí)2023-06-06linux的shell命令檢測某個java程序是否執(zhí)行
ps -ef |grep java|grep2016-04-04