javax.validation包里@NotNull等注解的使用方式
在做項目的時候,對pojo和傳入的參數(shù)進行校驗,如果是代碼編寫,需要很多if來判斷
其實可根據(jù)一些校驗的注解來實現(xiàn)我們的參數(shù)校驗,主要介紹一下常用的 javax.validation 這個倉庫的使用,這里總結(jié)一下
1、導包
在項目的pom.xml 文件夾中導入包
<!-- 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 依賴包含標準驗證注解
2、使用
這個包的注解主要有以下這么多,先簡單說明(以版本 號2.0.2為標準)
下面再分別介紹
| 注解名稱 | 驗證得類型 | 描述 |
|---|---|---|
| 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ù)值并驗證它們是嚴格負數(shù),不包括0 |
| NegativeOrZero | 數(shù)值 | 適用于數(shù)值并驗證它們是嚴格負數(shù),包括0 |
| NotBlank | CharSequence子類型 | 驗證注解的元素值不為空(不為null、去除首位空格后長度為0),不同于@NotEmpty,@NotBlank只應用于字符串且在比較時會去除字符串的首位空格 |
| NotEmpty | CharSequence子類型、Collection、Map、數(shù)組 | 驗證注解的元素值不為null且不為空(字符串長度不為0、集合大小不為0) |
| NotNull | 任意類型 | 被注解的元素屬性值必須非null |
| Null | 任意類型 | 被注解的元素屬性值必須為null |
| Pattern | String,任何CharSequence的子類型 | 被注解的元素值必須符合指定的正則表達式 |
| Positive | 數(shù)值 | 適用于數(shù)值并驗證它們是嚴格正數(shù),不包括0 |
| PositiveOrZero | 數(shù)值 | 適用于數(shù)值并驗證它們是嚴格正數(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)
// 這個表明可以重復使用,
//例如在一個field使用這個注解
// 一般注解只能使用一次
//@Max(value = 11)
//Long num;
// 如果使用了這個@Repeatable標注,那就可以使用兩次
//@Max(value = 11)
//@Max(value = 22)
//Long num;
@Repeatable(AssertFalse.List.class)
// 這個Constraint 指定驗證類,如果不自定義指定,實現(xià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的時候,其實默認是在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ī)則,如果不指定,就還是默認按照Default
這里需要注意:定義的組別最好要繼承Default接口,不然當我們在指定規(guī)則的校驗時候,那么不標注groups的屬性就不再校驗了,因為默認按照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 = "時間在當前時間之后才可以通過")
private Date date;
}
2)在接口中使用特定的組別。
兩個接:
- 創(chuàng)建user的時候,指定驗證的組別是CreateUser
- 更新user的時候,指定驗證的組別是UpdateUser
(當自定義的組別繼承了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 = "時間在當前時間之后才可以通過")
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風格
- 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
// 這里標注該注解是使用在filed和參數(shù)上的
@Target({ElementType.PARAMETER, ElementType.FIELD})
// 運行時生效
@Retention(RetentionPolicy.RUNTIME)
// 指定驗證的類是哪個 MyValidator 就是第二步做的事情
@Constraint(validatedBy = MyValidator.class)
public @interface MyValid {
// 提供自定義異常的信息,可以指定默認值
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ù)源代碼示例詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08
springboot結(jié)合ehcache防止惡意刷新請求的實現(xiàn)
這篇文章主要介紹了springboot結(jié)合ehcache防止惡意刷新請求的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-12-12
通過Java實現(xiàn)中文分詞與文本關(guān)鍵詞提取
這篇文章主要為大家詳細介紹了如何利用Java實現(xiàn)中文分詞以及文本關(guān)鍵詞提取功能,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習學習2023-06-06
linux的shell命令檢測某個java程序是否執(zhí)行
ps -ef |grep java|grep2016-04-04

