JAVA校驗(yàn)之@Valid和@Validated實(shí)踐指南
概述
區(qū)別
- 來(lái)源 (規(guī)范與框架綁定) -
@Validated: 它是 Spring 框架特有的注解,屬于 Spring 自身提供的一個(gè)功能增強(qiáng)。它基于 JSR 303 (Bean Validation 1.0) / JSR 380 (Bean Validation 2.0) 規(guī)范,但在其基礎(chǔ)上進(jìn)行了擴(kuò)展,特別適用于 Spring 環(huán)境。
-@Valid: 這是一個(gè)標(biāo)準(zhǔn)的 Java EE 注解,是 JSR 303 / JSR 380 (Bean Validation) 規(guī)范的核心組成部分。它不依賴于任何特定的框架,可以在任何支持 Bean Validation 的環(huán)境中(如 Hibernate Validator、CDI 等)使用。 - 注解位置 (適用范圍) -
@Validated: 主要用于類(聲明在類上時(shí),會(huì)對(duì)其所有公共方法參數(shù)進(jìn)行校驗(yàn))、方法和方法參數(shù)。它不能直接用于成員屬性(字段)。當(dāng)用于類上時(shí),通常與 Spring 的 AOP 攔截結(jié)合使用。
-@Valid: 可以用于方法、構(gòu)造函數(shù)、方法參數(shù)以及成員屬性(字段)。當(dāng)用于成員屬性時(shí),它會(huì)觸發(fā)對(duì)該屬性所引用對(duì)象的級(jí)聯(lián)校驗(yàn)。 - 分組 (靈活性) -
@Validated: 支持分組驗(yàn)證。這是其相比@Valid的一個(gè)重要優(yōu)勢(shì)。你可以通過(guò)在@Validated注解中指定一個(gè)或多個(gè)接口類來(lái)定義校驗(yàn)組,從而在不同的業(yè)務(wù)場(chǎng)景(如創(chuàng)建、更新)中應(yīng)用不同的校驗(yàn)規(guī)則。
-@Valid: 僅支持標(biāo)準(zhǔn)的 Bean Validation 功能,不支持分組驗(yàn)證。當(dāng)它觸發(fā)校驗(yàn)時(shí),它會(huì)執(zhí)行目標(biāo)對(duì)象上所有未指定分組的(即默認(rèn)組Default.class)以及任何被顯式指定為Default.class的約束。 - 嵌套驗(yàn)證 (遞歸校驗(yàn)) -
@Validated: 單獨(dú)使用時(shí),@Validated本身不支持嵌套驗(yàn)證。它通常需要與@Valid配合使用才能實(shí)現(xiàn)對(duì)復(fù)雜對(duì)象內(nèi)部嵌套屬性的校驗(yàn)。@Validated主要負(fù)責(zé)激活校驗(yàn)器并指定校驗(yàn)組,而@Valid則負(fù)責(zé)遞歸遍歷對(duì)象圖。
-@Valid: 支持嵌套驗(yàn)證。當(dāng)一個(gè)對(duì)象的屬性本身也是一個(gè)需要被校驗(yàn)的對(duì)象時(shí),你可以在該屬性上添加@Valid,這樣在父對(duì)象被校驗(yàn)時(shí),其內(nèi)部的嵌套屬性也會(huì)被自動(dòng)遞歸校驗(yàn)。
校驗(yàn)類型
@NotNull: 被注解的元素不能為null。
適用類型: 任何類型(對(duì)象、基本類型包裝類、集合、數(shù)組等)。@NotEmpty: 被注解的元素不能為null且其大小/長(zhǎng)度必須大于零。
適用類型:CharSequence(字符串)、Collection(集合如List,Set)、Map、Array(數(shù)組)。@NotBlank: 被注解的String類型不能為null,不能是空字符串 (""),且不能只包含空白字符(如空格、制表符)。
適用類型:CharSequence(字符串)。@Min(value): 被注解的元素必須是一個(gè)數(shù)字,且其值必須大于或等于指定值value。
適用類型:BigDecimal,BigInteger,Byte,Short,Integer,Long,Float,Double及其包裝類。@Max(value): 被注解的元素必須是一個(gè)數(shù)字,且其值必須小于或等于指定值value。
適用類型:BigDecimal,BigInteger,Byte,Short,Integer,Long,Float,Double及其包裝類。@DecimalMin(value, inclusive=true): 被注解的元素必須是一個(gè)數(shù)字,且其值必須大于或等于指定值value(默認(rèn)inclusive=true)。如果inclusive=false,則表示大于value。
適用類型:BigDecimal,BigInteger,Byte,Short,Integer,Long,Float,Double及其包裝類,以及String(其內(nèi)容必須是數(shù)字)。@DecimalMax(value, inclusive=true): 被注解的元素必須是一個(gè)數(shù)字,且其值必須小于或等于指定值value(默認(rèn)inclusive=true)。如果inclusive=false,則表示小于value。
適用類型:BigDecimal,BigInteger,Byte,Short,Integer,Long,Float,Double及其包裝類,以及String(其內(nèi)容必須是數(shù)字)。@Positive: 被注解的元素必須是正數(shù)(大于 0)。
適用類型:BigDecimal,BigInteger,Byte,Short,Integer,Long,Float,Double及其包裝類。@PositiveOrZero: 被注解的元素必須是正數(shù)或零(大于等于 0)。
適用類型:BigDecimal,BigInteger,Byte,Short,Integer,Long,Float,Double及其包裝類.@Negative: 被注解的元素必須是負(fù)數(shù)(小于 0)。
適用類型:BigDecimal,BigInteger,Byte,Short,Integer,Long,Float,Double及其包裝類.@NegativeOrZero: 被注解的元素必須是負(fù)數(shù)或零(小于等于 0)。
適用類型:BigDecimal,BigInteger,Byte,Short,Integer,Long,Float,Double及其包裝類.@Size(min, max): 被注解的元素大小/長(zhǎng)度必須在指定范圍(min到max)之內(nèi)。
適用類型:CharSequence(字符串)、Collection(集合)、Map、Array(數(shù)組)。@Digits(integer, fraction): 被注解的元素必須是一個(gè)數(shù)字,其整數(shù)部分的位數(shù)不能超過(guò)integer位,小數(shù)部分的位數(shù)不能超過(guò)fraction位。
適用類型:BigDecimal,BigInteger,Byte,Short,Integer,Long,Float,Double及其包裝類,以及String(其內(nèi)容必須是數(shù)字)。@Past: 被注解的日期或時(shí)間必須是一個(gè)過(guò)去的日期或時(shí)間。
適用類型:java.util.Date,java.util.Calendar,java.time.LocalDate,java.time.LocalDateTime,java.time.LocalTime,java.time.MonthDay,java.time.OffsetDateTime,java.time.OffsetTime,java.time.Year,java.time.YearMonth,java.time.ZonedDateTime等。@PastOrPresent: 被注解的日期或時(shí)間必須是一個(gè)過(guò)去的或當(dāng)前的日期或時(shí)間。
適用類型: 同@Past。@Future: 被注解的日期或時(shí)間必須是一個(gè)未來(lái)的日期或時(shí)間。
適用類型: 同@Past。@FutureOrPresent: 被注解的日期或時(shí)間必須是一個(gè)未來(lái)的或當(dāng)前的日期或時(shí)間。
適用類型: 同@Past。@Pattern(regexp, flags=Pattern.CASE_INSENSITIVE): 被注解的String必須符合指定的正則表達(dá)式regexp。
適用類型:CharSequence(字符串)。@Email: 被注解的String必須是有效的郵件地址格式。
適用類型:CharSequence(字符串)。@AssertTrue: 被注解的布爾類型必須為true。通常用于驗(yàn)證復(fù)雜邏輯(如兩個(gè)字段間的關(guān)聯(lián)驗(yàn)證),需要一個(gè)返回boolean的 getter 方法。
適用類型:Boolean或boolean。@AssertFalse: 被注解的布爾類型必須為false。
適用類型:Boolean或boolean。
實(shí)踐
分組校驗(yàn)
分組校驗(yàn)允許你根據(jù)不同的業(yè)務(wù)場(chǎng)景(如創(chuàng)建、更新、刪除等)對(duì)同一個(gè)實(shí)體類應(yīng)用不同的驗(yàn)證規(guī)則,從而實(shí)現(xiàn)更靈活和精細(xì)的控制。
特別注意:
- 定義分組必須使用接口。 這些接口是空的,僅作為校驗(yàn)規(guī)則的標(biāo)識(shí)。
- 要校驗(yàn)的字段上的注解必須通過(guò)
groups屬性指定所屬的分組。 如果一個(gè)校驗(yàn)注解沒(méi)有指定groups,它將默認(rèn)屬于Default.class組,并在任何未指定分組或顯式指定Default.class的校驗(yàn)中生效。 - 在 Controller 方法的
@Validated注解中指定要激活的分組。
創(chuàng)建分組
// 用于創(chuàng)建操作的校驗(yàn)組
public interface CreationGroup { }
// 用于更新操作的校驗(yàn)組
public interface UpdateGroup { }
// 你還可以定義其他分組,例如:
// public interface DeleteGroup { }
設(shè)置實(shí)體
在實(shí)體類的字段上添加校驗(yàn)注解,并使用 groups 屬性指定該校驗(yàn)規(guī)則屬于哪個(gè)分組。一個(gè)字段可以屬于多個(gè)分組。
@Data
public class UserBean {
// 創(chuàng)建和更新時(shí)都校驗(yàn)用戶名非空
// 默認(rèn)的 Default 組也適用,但如果指定了分組,則只在指定分組下生效
@NotEmpty(message = "用戶名不能為空", groups = {CreationGroup.class, UpdateGroup.class})
private String username;
// 年齡校驗(yàn),無(wú)分組,默認(rèn)在所有場(chǎng)景(包括未指定分組的 @Validated)下都校驗(yàn)
@Min(value = 18, message = "年齡不能小于18歲")
private Integer age;
// 郵箱格式校驗(yàn),無(wú)分組,默認(rèn)在所有場(chǎng)景下都校驗(yàn)
@Email(message = "郵箱格式不正確")
private String email;
// ID 校驗(yàn),只在更新時(shí)生效,且必須大于等于1
@NotNull(message = "ID不能為空", groups = UpdateGroup.class) // 更新時(shí)ID不能為null
@Min(value = 1, message = "ID必須大于0", groups = {UpdateGroup.class})
private Long id;
// ... 其他屬性
}
使用校驗(yàn)
在 Controller 方法的參數(shù)上使用 @Validated 注解,并傳入需要激活的校驗(yàn)組。
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid; // 導(dǎo)入 javax.validation.Valid
@RestController
@RequestMapping("validation")
public class ValidationController {
/**
* 更新用戶接口:只激活 UpdateGroup 組的校驗(yàn)規(guī)則。
* 此時(shí),UserBean 中的 id 字段會(huì)根據(jù) UpdateGroup 的規(guī)則進(jìn)行校驗(yàn)。
* username 和 age、email 字段則會(huì)根據(jù)它們自身定義的(或默認(rèn)的)規(guī)則進(jìn)行校驗(yàn)。
*/
@GetMapping("updateUser")
public UserBean updateUser(@Validated({UpdateGroup.class}) UserBean userBean){
// 如果校驗(yàn)失敗,會(huì)拋出 MethodArgumentNotValidException,由全局異常處理器捕獲
System.out.println("更新用戶成功: " + userBean);
return userBean;
}
/**
* 創(chuàng)建用戶接口:只激活 CreationGroup 組的校驗(yàn)規(guī)則。
* 此時(shí),UserBean 中的 username 字段會(huì)根據(jù) CreationGroup 的規(guī)則進(jìn)行校驗(yàn)。
* age 和 email 字段則會(huì)根據(jù)它們自身定義的(或默認(rèn)的)規(guī)則進(jìn)行校驗(yàn)。
* id 字段由于不屬于 CreationGroup,將不會(huì)被校驗(yàn)。
*/
@GetMapping("createUser")
public UserBean createUser(@Validated({CreationGroup.class}) UserBean userBean){
// 如果校驗(yàn)失敗,會(huì)拋出 MethodArgumentNotValidException,由全局異常處理器捕獲
System.out.println("創(chuàng)建用戶成功: " + userBean);
return userBean;
}
// 注意:如果一個(gè)方法參數(shù)不加 @Validated,但實(shí)體類中有 @Valid 注解,
// 則會(huì)默認(rèn)校驗(yàn)所有無(wú)分組的字段(即 Default 組)。
// 例如:
// @PostMapping("defaultValid")
// public UserBean defaultValid(@Valid @RequestBody UserBean userBean) {
// return userBean;
// }
}
嵌套驗(yàn)證
嵌套校驗(yàn)(Nested Validation) 指的是在驗(yàn)證外部對(duì)象時(shí),對(duì)其內(nèi)部包含的其他對(duì)象進(jìn)行遞歸驗(yàn)證的過(guò)程。當(dāng)一個(gè)對(duì)象中包含另一個(gè)對(duì)象作為屬性,并且需要對(duì)這個(gè)被包含的對(duì)象也進(jìn)行驗(yàn)證時(shí),就需要進(jìn)行嵌套校驗(yàn)。
嵌套屬性指的是在一個(gè)對(duì)象中包含另一個(gè)對(duì)象作為其屬性的情況。換句話說(shuō),當(dāng)一個(gè)對(duì)象的屬性本身又是一個(gè)對(duì)象(非基本類型),那么這些被包含的對(duì)象就可以稱為嵌套屬性。
特別提示:
- 想要嵌套校驗(yàn)生效,必須在嵌套屬性上加 @Valid 注解。這個(gè)注解告訴校驗(yàn)器深入到這個(gè)屬性對(duì)應(yīng)的對(duì)象內(nèi)部進(jìn)行校驗(yàn)。
- @Valid 注解默認(rèn)校驗(yàn)無(wú)分組的項(xiàng)。它會(huì)觸發(fā)嵌套對(duì)象上所有屬于
Default.class組的校驗(yàn)。 - 嵌套驗(yàn)證要支持分組,需要結(jié)合 @Validated({YourGroup.class})。 當(dāng)
@Validated指定了分組并作用于包含@Valid屬性的外部對(duì)象時(shí),@Valid會(huì)將這個(gè)分組信息傳遞給嵌套對(duì)象,從而使其內(nèi)部只有對(duì)應(yīng)分組的校驗(yàn)規(guī)則生效。
驗(yàn)證嵌套屬性
首先,定義你的嵌套實(shí)體類,并為其字段添加校驗(yàn)規(guī)則:
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class AddressBean {
@NotBlank(message = "國(guó)家不能為空")
private String country;
@NotBlank(message = "城市不能為空")
private String city;
}
接著,在主實(shí)體類中包含這個(gè)嵌套屬性,并在該屬性上添加 @Valid 和 @NotNull(如果地址對(duì)象本身不能為 null):
@Data
public class UserBean {
@NotEmpty(message = "用戶名不能為空", groups = {CreationGroup.class})
private String username;
@Min(value = 18, message = "年齡不能小于18歲")
private Integer age;
@Email(message = "郵箱格式不正確")
private String email;
// 嵌套驗(yàn)證必須要加上 @Valid
// 同時(shí)可以添加 @NotNull 來(lái)確保 address 對(duì)象本身不為空
@Valid
@NotNull(message = "地址信息不能為空")
private AddressBean address; // 嵌套屬性
// ... 其他屬性和 getter/setter
}
然后在 Controller 中使用 @Validated 來(lái)觸發(fā)校驗(yàn):
@RestController
@RequestMapping("validation")
public class ValidationController {
/**
* 嵌套驗(yàn)證示例。
* 當(dāng) @RequestBody UserBean userBean 傳入時(shí),
* 如果 userBean 內(nèi)部的 address 屬性有 @Valid 注解,
* 那么 address 對(duì)象自身的校驗(yàn)規(guī)則也會(huì)被觸發(fā)。
* 此處如果 address 為 null 或其內(nèi)部字段(country, city)為空,都會(huì)觸發(fā)校驗(yàn)失敗。
*/
@PostMapping("nestValid")
public UserBean nestValid(@Validated @RequestBody UserBean userBean){ // @Validated 也可以不指定分組,則校驗(yàn) Default 組
System.out.println("嵌套驗(yàn)證成功: " + userBean);
return userBean;
}
/**
* 帶分組的嵌套驗(yàn)證示例。
* 此時(shí),只有 UserBean 中屬于 CreationGroup 的校驗(yàn)規(guī)則生效,
* 并且這個(gè)分組也會(huì)傳遞給嵌套的 AddressBean,如果 AddressBean 中也有分組校驗(yàn)規(guī)則,它們就會(huì)生效。
* 此外,因?yàn)?address 屬性是 @Valid 且 @NotNull,它會(huì)始終被校驗(yàn)。
*/
@PostMapping("nestValidWithGroup")
public UserBean nestValidWithGroup(@Validated(CreationGroup.class) @RequestBody UserBean userBean){
System.out.println("帶分組的嵌套驗(yàn)證成功: " + userBean);
return userBean;
}
}
驗(yàn)證集合
當(dāng)請(qǐng)求體是一個(gè)對(duì)象的集合(如 List<YourBean>)時(shí),校驗(yàn)的原理與嵌套驗(yàn)證類似,你需要將 @Valid 放在集合參數(shù)前,而 @Validated 則用于指定校驗(yàn)組(如果需要)。同時(shí),你也可以對(duì)集合本身進(jìn)行非空校驗(yàn)。
@RestController
@RequestMapping("/metadataTemplate")
public class ReMetadataTemplateController {
// 假設(shè) reMetadataTemplateService 已注入
/**
* 更改元數(shù)據(jù)模板狀態(tài)接口,支持對(duì)列表本身非空及列表中每個(gè)對(duì)象的字段進(jìn)行分組校驗(yàn)。
*
* @param list 待更新的元數(shù)據(jù)模板列表
* @return 結(jié)果
*/
@PutMapping("/changeStatus")
@Validated(EditGroup.class) // 指定激活 EditGroup 組的校驗(yàn)
public Void changeStatus(
// @Valid 觸發(fā)對(duì) List 中每個(gè) ReMetadataTemplateBo 對(duì)象的校驗(yàn)
// @NotEmpty(message = "參數(shù)不能為空") 校驗(yàn) List 本身不能為 null 或空集合
@Valid @NotEmpty(message = "元數(shù)據(jù)模板列表不能為空", groups = EditGroup.class)
@RequestBody List<ReMetadataTemplateBo> list
) {
// 如果校驗(yàn)失敗,會(huì)拋出 MethodArgumentNotValidException,由全局異常處理器捕獲
// ... 業(yè)務(wù)邏輯
reMetadataTemplateService.changeStatus(list);
}
}
異常處理
為了更好地處理校驗(yàn)失敗時(shí)拋出的異常,通常需要配置一個(gè)全局異常處理器。
引用
- https://cloud.tencent.com/developer/article/2396081
- Spring Boot Validation with @Valid and @Validated
- JSR 303: Bean Validation
- JSR 380: Bean Validation 2.0
到此這篇關(guān)于JAVA校驗(yàn)之@Valid和@Validated實(shí)踐指南的文章就介紹到這了,更多相關(guān)JAVA校驗(yàn)@Valid和@Validated內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 使用@Validated和@Valid 解決list校驗(yàn)的問(wèn)題
- Java中的三種校驗(yàn)注解的使用(@Valid,@Validated和@PathVariable)
- spring @Validated 注解開發(fā)中使用group分組校驗(yàn)的實(shí)現(xiàn)
- Java參數(shù)校驗(yàn)@Validated、@Valid介紹及使用詳解
- SpringBoot參數(shù)校驗(yàn)之@Validated的使用詳解
- @Valid和@Validated注解校驗(yàn)以及異常處理方式
- 使用@Validated注解進(jìn)行校驗(yàn)卻沒(méi)有效果的解決
- Spring 中@Validated 分組校驗(yàn)的使用解析
- Spring利用@Validated注解實(shí)現(xiàn)參數(shù)校驗(yàn)詳解
相關(guān)文章
Spring事務(wù)失效的一種原因關(guān)于this調(diào)用的問(wèn)題
這篇文章主要介紹了Spring事務(wù)失效的一種原因關(guān)于this調(diào)用的問(wèn)題,本文給大家分享問(wèn)題原因及解決辦法,通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-10-10
springboot vue完成發(fā)送接口請(qǐng)求顯示響應(yīng)頭信息
這篇文章主要為大家介紹了springboot+vue完成發(fā)送接口請(qǐng)求顯示響應(yīng)頭信息,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
IntelliJ中高效重構(gòu)的10個(gè)快捷方式詳解
這篇文章主要為大家介紹了IntelliJ中高效重構(gòu)的10個(gè)快捷方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
SpringBoot配置線程池的實(shí)現(xiàn)示例
本文主要介紹了SpringBoot配置線程池的實(shí)現(xiàn)示例,主要包括在Spring Boot中創(chuàng)建和配置線程池,包括設(shè)置線程池的大小、隊(duì)列容量、線程名稱等參數(shù),感興趣的可以了解一下2023-09-09
springboot讀取.properties配置文件中的map和list類型配置參數(shù)方式
這篇文章主要介紹了springboot讀取.properties配置文件中的map和list類型配置參數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03
SpringMVC 向jsp頁(yè)面?zhèn)鬟f數(shù)據(jù)庫(kù)讀取到的值方法
下面小編就為大家分享一篇SpringMVC 向jsp頁(yè)面?zhèn)鬟f數(shù)據(jù)庫(kù)讀取到的值方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-03-03

