javax.validation包里@NotNull等注解的使用方式
在做項(xiàng)目的時(shí)候,對(duì)pojo和傳入的參數(shù)進(jìn)行校驗(yàn),如果是代碼編寫(xiě),需要很多if來(lái)判斷
其實(shí)可根據(jù)一些校驗(yàn)的注解來(lái)實(shí)現(xiàn)我們的參數(shù)校驗(yàn),主要介紹一下常用的 javax.validation 這個(gè)倉(cāng)庫(kù)的使用,這里總結(jié)一下
1、導(dǎo)包
在項(xiàng)目的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>
這個(gè)驗(yàn)證根據(jù)JSR 380規(guī)范,validation-api 依賴包含標(biāo)準(zhǔn)驗(yàn)證注解
2、使用
這個(gè)包的注解主要有以下這么多,先簡(jiǎn)單說(shuō)明(以版本 號(hào)2.0.2為標(biāo)準(zhǔn))
下面再分別介紹
| 注解名稱 | 驗(yàn)證得類型 | 描述 |
|---|---|---|
| AssertFalse | Boolean,boolean | 被注解的元素屬性值必須為false |
| AssertTrue | Boolean,boolean | 被注解的元素屬性值必須為true |
| DecimalMax | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存儲(chǔ)的是數(shù)字)子類型 | 驗(yàn)證注解的元素值小于等于指定的value值 |
| DecimalMin | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存儲(chǔ)的是數(shù)字)子類型 | 驗(yàn)證注解的元素值大于等于指定的value值 |
| Digits | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存儲(chǔ)的是數(shù)字)子類型 | 驗(yàn)證注解的元素值的整數(shù)位數(shù)和小數(shù)位數(shù)上限 |
| Max | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存儲(chǔ)的是數(shù)字)子類型 | 被注解的元素值的類型必須為數(shù)字,其值必須小于等于指定的最大值 |
| Min | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存儲(chǔ)的是數(shù)字)子類型 | 被注解的元素值的類型必須為數(shù)字,其值必須大于等于指定的最小值 |
| CharSequence子類型(如String) | ||
| Future | java.util.Date,java.util.Calendar;Joda Time類庫(kù)的日期類型 | 被注解的元素值得范圍必須為未來(lái)的一個(gè)時(shí)間 |
| FutureOrPresent | java.util.Date,java.util.Calendar;Joda Time類庫(kù)的日期類型 | 被注解的元素值得范圍必須為未來(lái)的一個(gè)時(shí)間或者現(xiàn)在得時(shí)間 |
| Past | java.util.Date,java.util.Calendar;Joda Time類庫(kù)的日期類型 | 被注解的元素的值范圍必須為過(guò)去的一個(gè)時(shí)間 |
| PastOrPresent | java.util.Date,java.util.Calendar;Joda Time類庫(kù)的日期類型 | 被注解的元素的值范圍必須為過(guò)去或者現(xiàn)在的一個(gè)時(shí)間 |
| Negative | 數(shù)值 | 適用于數(shù)值并驗(yàn)證它們是嚴(yán)格負(fù)數(shù),不包括0 |
| NegativeOrZero | 數(shù)值 | 適用于數(shù)值并驗(yàn)證它們是嚴(yán)格負(fù)數(shù),包括0 |
| NotBlank | CharSequence子類型 | 驗(yàn)證注解的元素值不為空(不為null、去除首位空格后長(zhǎng)度為0),不同于@NotEmpty,@NotBlank只應(yīng)用于字符串且在比較時(shí)會(huì)去除字符串的首位空格 |
| NotEmpty | CharSequence子類型、Collection、Map、數(shù)組 | 驗(yàn)證注解的元素值不為null且不為空(字符串長(zhǎng)度不為0、集合大小不為0) |
| NotNull | 任意類型 | 被注解的元素屬性值必須非null |
| Null | 任意類型 | 被注解的元素屬性值必須為null |
| Pattern | String,任何CharSequence的子類型 | 被注解的元素值必須符合指定的正則表達(dá)式 |
| Positive | 數(shù)值 | 適用于數(shù)值并驗(yàn)證它們是嚴(yán)格正數(shù),不包括0 |
| PositiveOrZero | 數(shù)值 | 適用于數(shù)值并驗(yàn)證它們是嚴(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)一起指定注釋要保留多長(zhǎng)時(shí)間
@Retention(RetentionPolicy.RUNTIME)
//表明這個(gè)注解是由 javadoc記錄的
@Documented
public @interface MyOwnAnnotation {
}
2.1 注解詳細(xì)屬性的介紹
查看上面所有注解的源碼,關(guān)于注解中的字段,主要分為以下兩種
2.1.1 不能自定義值的
以下面的這些注解為代表
- AssertFalse AssertTrue
- PositiveOrZero Positive Negative NegativeOrZero
- NotNull Null NotEmpty NotBlank
- FutureOrPresent Future PastOrPresent Past
以 @AssertFalse為例,該注解的詳細(xì)內(nèi)容(源碼)
@Documented
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
// 這個(gè)表明可以重復(fù)使用,
//例如在一個(gè)field使用這個(gè)注解
// 一般注解只能使用一次
//@Max(value = 11)
//Long num;
// 如果使用了這個(gè)@Repeatable標(biāo)注,那就可以使用兩次
//@Max(value = 11)
//@Max(value = 22)
//Long num;
@Repeatable(AssertFalse.List.class)
// 這個(gè)Constraint 指定驗(yàn)證類,如果不自定義指定,實(shí)現(xiàn)其默認(rèn)的驗(yàn)證類 ,如果指定
// @Constraint(validatedBy = { MyValidator.class })
@Constraint(validatedBy = {})
public @interface AssertFalse {
// 校驗(yàn)失敗的信息,可以自定義
String message() default "{javax.validation.constraints.AssertFalse.message}";
// 這里主要進(jìn)行將validator進(jìn)行分類,不同的類group中會(huì)執(zhí)行不同的validator操作
Class<?>[] groups() default {};
// 這個(gè)主要是指定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 能自定義值的
其他的注解都是可以自定義一些值,作為校驗(yàn)的參照值
以 @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 {
// 校驗(yàn)失敗的信息,可以自定義
String message() default "{javax.validation.constraints.Max.message}";
// 這里主要進(jìn)行將validator進(jìn)行分類,不同的類group中會(huì)執(zhí)行不同的validator操作
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
// 與上面不同的就是多了個(gè)value,這個(gè)需要自定義參數(shù)傳入的值與這個(gè)value值對(duì)比,也就是校驗(yàn)的參考值
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屬性的使用
這個(gè)屬性的作用就是舉個(gè)例子:例如我們?cè)趥鲄⒌臅r(shí)候,加入我們創(chuàng)建一個(gè)用戶,那么我們?cè)賯魅氲膮?shù)是不會(huì)包含用戶的id,而如果更新這個(gè)用戶,我們就需要傳入用戶id,那么根據(jù)多種校驗(yàn)規(guī)則的時(shí)候,這個(gè)屬性就起作用了。
如果我們?cè)谝粋€(gè)屬性使用注解的時(shí)候,如果不指定groups的時(shí)候,其實(shí)默認(rèn)是在Default 組別中
@NotNull(message = "姓名不能為空")
private String name;
等同于
import javax.validation.groups.Default;
@NotNull(message = "姓名不能為空" , groups = {Default.class})
private String name;
那么根據(jù)這個(gè)情況,如果我們想自定義組別的時(shí)候,我們就可以分為以下幾個(gè)步驟:
- 定義組別
- 使用組別
(1)定義組別
還是這個(gè)例子:如果創(chuàng)建一個(gè)用戶,不用傳入用戶的id,如果更新這個(gè)用戶,我們就需要傳入用戶id
那么我們就定義兩個(gè)組別,創(chuàng)建用戶組別、更新用戶組別(其實(shí)就是兩個(gè)接口,里面不用有方法,但是要注意要繼承Default接口,理由下面會(huì)說(shuō))
import javax.validation.groups.Default;
public interface CreateUser extends Default {
}
import javax.validation.groups.Default;
public interface UpdateUser extends Default{
}
(2)使用組別
1)首先在pojo中,我們需要使用組別,若屬性在不同的組別有不同的校驗(yàn)方式,那么就特殊指定其需要校驗(yàn)的規(guī)則,如果不指定,就還是默認(rèn)按照Default
這里需要注意:定義的組別最好要繼承Default接口,不然當(dāng)我們?cè)谥付ㄒ?guī)則的校驗(yàn)時(shí)候,那么不標(biāo)注groups的屬性就不再校驗(yàn)了,因?yàn)槟J(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 = "最大長(zhǎng)度為20")
private String address;
@Email(message = "不滿足郵箱格式")
private String email;
@AssertTrue(message = "字段為true才能通過(guò)")
private boolean isAuth;
@NotBlank(message = "手機(jī)號(hào)不能為空")
@Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手機(jī)號(hào)格式有誤")
private String mobile;
@Future(message = "時(shí)間在當(dāng)前時(shí)間之后才可以通過(guò)")
private Date date;
}
2)在接口中使用特定的組別。
兩個(gè)接:
- 創(chuàng)建user的時(shí)候,指定驗(yàn)證的組別是CreateUser
- 更新user的時(shí)候,指定驗(yàn)證的組別是UpdateUser
(當(dāng)自定義的組別繼承了Default,這里指定驗(yàn)證組別的,屬性會(huì)根據(jù)指定的groups來(lái)進(jìn)行校驗(yàn),那么沒(méi)有指定groups的都會(huì)校驗(yàn),如果不繼承Default,那沒(méi)有自定義groups為這個(gè)組別的屬性就不生效了)
/**
* 走參數(shù)校驗(yàn)注解的 groups 組合校驗(yàn)
*
* @param user
* @return
*/
@PostMapping("/users/update")
public ResponseDTO updateUser(@RequestBody @Validated(UpdateUser.class) User user) {
userService.updateById(userDTO);
return ResponseDTO.success();
}
/**
* 走參數(shù)校驗(yàn)注解的 groups 組合校驗(yàn)
*
* @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 定義驗(yàn)證規(guī)則以
- pojo作為參數(shù)傳參
(1)User pojo類 定義校驗(yàn)規(guī)則
@Data
class User{
@NotNull(message = "姓名不能為空")
private String name;
@NotNull(message = "性別不能為空")
private String sex;
@Max(value = 20 ,message = "最大長(zhǎng)度為20")
private String address;
@Email(message = "不滿足郵箱格式")
private String email;
@AssertTrue(message = "字段為true才能通過(guò)")
private boolean isAuth;
@NotBlank(message = "手機(jī)號(hào)不能為空")
@Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手機(jī)號(hào)格式有誤")
private String mobile;
@Future(message = "時(shí)間在當(dāng)前時(shí)間之后才可以通過(guò)")
private Date date;
}
(2)讓校驗(yàn)規(guī)則生效。在Controller類的時(shí)候,我們只需要利用 @Validated 注解來(lái)實(shí)現(xiàn)pojo的校驗(yàn)
@RequestMapping("users")
public ResponseDTO saveUser( @RequestBody @Validated User user){
}
(3)捕捉驗(yàn)證異常。如果參數(shù)校驗(yàn)通過(guò),那么就直接執(zhí)行接口方法,但是如果失敗了,我們?nèi)绾芜M(jìn)行處理
MethodArgumentNotValidException是springBoot中進(jìn)行綁定參數(shù)校驗(yàn)時(shí)的異常,需要在springBoot中處理
其他需要處理 ConstraintViolationException異常進(jìn)行處理
一般像異常捕捉的,可以自定義一個(gè)異常捕捉Handler,實(shí)現(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ù)校驗(yàn)
*/
@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, "不好意思,路徑不存在,請(qǐng)檢查路徑是否正確");
}
/**
* 所有其他異常捕捉
*/
@ExceptionHandler(Exception.class)
public ResponseDTO handleException(Exception e) {
logger.error(e.getMessage(), e);
return new ResponseDTO(500, "不好意思,系統(tǒng)繁忙,請(qǐ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、自定義使用
如果我們想自定義一個(gè)驗(yàn)證的注解,那么需要怎么做呢?
- 定義一個(gè)注解
- 編寫(xiě)一個(gè)驗(yàn)證類
- 使用
**(1)**我們首先定義一個(gè)像上面的@Null 這樣的注解
@Documented
// 這里標(biāo)注該注解是使用在filed和參數(shù)上的
@Target({ElementType.PARAMETER, ElementType.FIELD})
// 運(yùn)行時(shí)生效
@Retention(RetentionPolicy.RUNTIME)
// 指定驗(yàn)證的類是哪個(gè) MyValidator 就是第二步做的事情
@Constraint(validatedBy = MyValidator.class)
public @interface MyValid {
// 提供自定義異常的信息,可以指定默認(rèn)值
String message() default "參數(shù)不合法";
// 分組,詳細(xì)看 上面的介紹
Class<?>[] groups() default {};
// 針對(duì)bean的,使用不多
Class<? extends Payload>[] payload() default {};
}
(2)編寫(xiě)一個(gè)驗(yàn)證類
我們需要編寫(xiě)一個(gè) 實(shí)現(xiàn) ConstraintValidator 類實(shí)現(xiàn)類,來(lái)指定我們的校驗(yàn)規(guī)則
如果不指定特定注解的情況下,直接使用
// 這個(gè)是Max的指定的驗(yàn)證規(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);
}
// 自定義的驗(yàn)證類
public class MyValidator implements ConstraintValidator {
@Override
public void initialize(Annotation constraintAnnotation) {
// 可以獲取注解的值 ,一般寫(xiě)在該方法中
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
// 編寫(xiě)自己屬性驗(yàn)證的規(guī)則,o 則是待驗(yàn)證的值
return false;
}
}
如果在指定特定注解的情況下,那么我們就可特定 注解
// 自定義的驗(yàn)證類
public class MyValidator implements ConstraintValidator<MyValid , Object> {
@Override
public void initialize(MyValid myValid) {
// 可以獲取注解的值 ,一般寫(xiě)在該方法中
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
// 編寫(xiě)自己屬性驗(yàn)證的規(guī)則,o 則是待驗(yàn)證的值
return false;
}
}
(3)使用。就跟正常的那些注解一樣使用即可。
@Data
public Class Example{
@NotBlank(message = "姓名不能為空")
@MyValid(message = "姓名有誤,請(qǐng)核對(duì)后提交")
private String name;
}
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
springMvc請(qǐng)求的跳轉(zhuǎn)和傳值的方法
本篇文章主要介紹了springMvc請(qǐng)求的跳轉(zhuǎn)和傳值的方法,這里整理了幾種跳轉(zhuǎn)方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-02-02
java服務(wù)器的簡(jiǎn)單實(shí)現(xiàn)過(guò)程記錄
在線瀏覽網(wǎng)頁(yè)離不開(kāi)服務(wù)器,用戶發(fā)出請(qǐng)求request,服務(wù)器做出響應(yīng)response,提供給用戶需要的頁(yè)面,這篇文章主要給大家介紹了關(guān)于java服務(wù)器簡(jiǎn)單實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2021-11-11
Springboot整合多數(shù)據(jù)源代碼示例詳解
這篇文章主要介紹了Springboot整合多數(shù)據(jù)源代碼示例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
springboot結(jié)合ehcache防止惡意刷新請(qǐng)求的實(shí)現(xiàn)
這篇文章主要介紹了springboot結(jié)合ehcache防止惡意刷新請(qǐng)求的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
通過(guò)Java實(shí)現(xiàn)中文分詞與文本關(guān)鍵詞提取
這篇文章主要為大家詳細(xì)介紹了如何利用Java實(shí)現(xiàn)中文分詞以及文本關(guān)鍵詞提取功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)學(xué)習(xí)2023-06-06
淺談MyBatis 如何執(zhí)行一條 SQL語(yǔ)句
Mybatis 是 Java 開(kāi)發(fā)中比較常用的 ORM 框架。在日常工作中,我們都是直接通過(guò) Spring Boot 自動(dòng)配置,并直接使用,但是卻不知道 Mybatis 是如何執(zhí)行一條 SQL 語(yǔ)句的,下面就一起講解一下2021-05-05
linux的shell命令檢測(cè)某個(gè)java程序是否執(zhí)行
ps -ef |grep java|grep2016-04-04

