Spring Validation實踐及其實現原理

Bean Validation 2.0 注解

校驗空值
@Null:驗證對象是否為 null
@NotNull:驗證對象是否不為 null
@NotEmpty:驗證對象不為 null,且長度(數組、集合、字符串等)大于 0
@NotBlank:驗證字符串不為 null,且去除兩端空白字符后長度大于 0
校驗大小
@Size(min=, max=):驗證對象(數組、集合、字符串等)長度是否在給定的范圍之內
@Min(value):驗證數值(整數或浮點數)是否大于等于指定的最小值
@Max(value):驗證數值是否小于等于指定的最大值
校驗布爾值
@AssertTrue:驗證 Boolean 對象是否為 true
@AssertFalse:驗證 Boolean 對象是否為 false
校驗日期和時間
@Past:驗證 DateCalendar 對象是否在當前時間之前
@Future:驗證 DateCalendar 對象是否在當前時間之后
@PastOrPresent:驗證日期是否是過去或現在的時間
@FutureOrPresent:驗證日期是否是現在或將來的時間
正則表達式
@Pattern(regexp=, flags=):驗證 String 對象是否符合正則表達式的規則
Hibernate Validation 拓展
@Length(min=, max=):驗證字符串的大小是否在指定的范圍內
@Range(min=, max=):驗證數值是否在合適的范圍內
@UniqueElements:校驗集合中的值是否唯一,依賴于 equals 方法
@ScriptAssert:利用腳本進行校驗

@Valid 和 @Validated
這兩個注解是校驗的入口,作用相似但用法上存在差異。

@Validated

// 用于類/接口/枚舉,方法以及參數
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})  
@Retention(RetentionPolicy.RUNTIME)
@Documented  
public @interface Validated {  // 校驗時啟動的分組  Class<?>[] value() default {};  
}

@Valid

// 用于方法,字段,構造函數,參數,以及泛型類型  
@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE })  
@Retention(RUNTIME)  
@Documented
public @interface Valid {  // 未提供其他屬性  
}

作用范圍不同:@Validated 無法作用在于字段, @Valid 無法作用于類;
注解中的屬性不同:@Validated 中提供了指定校驗分組的屬性,而 @Valid 沒有這個功能,因為 @Valid 不能進行分組校驗。
字段校驗場景及使用示例
常見的校驗場景有三種: Controller 層的校驗、編程式校驗、 Dubbo 接口校驗。

Controller層 的校驗
使用方式
當方法入參為 @RequestBody 注解的 JavaBean,可在入參前使用 @Validated 或 @Valid 注解開啟校驗。

@PostMapping("/save")
public Response<Boolean> saveNotice(@Validated @RequestBody NoticeDTO noticeDTO) {// noticeDTO中各字段校驗通過,才會執行后續業務邏輯return Response.ok(true);
}

當方法入參為 @PathVariable、 @RequestParam 注解的簡單參數時,需要在 Controller 加上 @Validated 注解開啟校驗。

@RequestMapping("/notice")
@RestController
// 必須加上該注解
@Validated
public class UserController {// 路徑變量@GetMapping("{id}")public Reponse<NoticeDTO> detail(@PathVariable("id") @Min(1L) Long noticeId) {// 參數noticeId校驗通過,執行后續業務邏輯return Reponse.ok();}// 請求參數@GetMapping("getByTitle")public Result getByTitle(@RequestParam("title") @Length(min = 1, max = 20) String  title) {// 參數title校驗通過,執行后續業務邏輯return Result.ok();}
}

原理
Spring 框架中的 HandlerMethodArgumentResolver 策略接口,負責將方法參數解析為特定請求中的參數值。

public interface HandlerMethodArgumentResolver {  // 判斷當前解析器是否支持給定的方法參數boolean supportsParameter(MethodParameter var1);  @Nullable  // 實際解析參數的方法Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception;
}
上述接口針對 @RequestBody 的實現類 RequestResponseBodyMethodProcessor 中,存在字段校驗邏輯,調用 validateIfApplicable 方法校驗參數。
// RequestResponseBodyMethodProcessor
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {  // 前置處理// 校驗邏輯if (binderFactory != null) {  WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);  if (arg != null) {//調用校驗函數this.validateIfApplicable(binder, parameter);  if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {  throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());  }  }// 數據綁定邏輯}// 返回處理結果return this.adaptArgumentIfNecessary(arg, parameter);  
}

validateIfApplicable 方法中,根據方法參數上的注解,決定是否進行字段校驗:當存在 @Validated 或以 Valid 開頭的注解時,進行校驗。

protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {// 獲取參數上的注解Annotation[] annotations = parameter.getParameterAnnotations();  Annotation[] var4 = annotations;  int var5 = annotations.length;  // 遍歷注解for(int var6 = 0; var6 < var5; ++var6) {  Annotation ann = var4[var6];  // 獲取 @Validated 注解Validated validatedAnn = (Validated)AnnotationUtils.getAnnotation(ann, Validated.class); // 或者注解以 Valid 開頭 if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {// 開啟校驗Object hints = validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann);  Object[] validationHints = hints instanceof Object[] ? (Object[])((Object[])hints) : new Object[]{hints};  binder.validate(validationHints);  break;  }  }  
}

@PathVariable 和 @RequestParam 對應的實現類中,則沒有相應字段校驗邏輯,因此需要在 Controller 上使用 @Validated,開啟字段校驗。

編程式校驗
配置 Validator

@Configuration  
public class ValidatorConfiguration {  @Bean  public Validator validator() {  ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)  .configure()  // 設置是否開啟快速失敗模式  //.failFast(true)  .buildValidatorFactory();  return validatorFactory.getValidator();  }  
}
獲取 validator 并校驗
public class TestValidator {// 注入驗證器@Resourceprivate javax.validation.Validator validator;public String testMethod(TestRequest request) {// 進行校驗,獲取校驗結果Set<ConstraintViolation<TestRequest>> constraintViolations = validator.validate(request);// 組裝校驗信息并返回return res;}
}
Dubbo 接口校驗
可在 @DubboService 注解中,設置 validation 參數為 true,開啟生產者的字段驗證。
@DubboService(version = "1.0.0", validation="true")
public class DubboApiImpl implements DubboApi {....
}

該方式返回的信息對使用者不友好,可通過 Dubbo 的 filter 自定義校驗邏輯和返回信息。需要注意的是,在 Dubbo 中有自己的 IOC 實現來控制容器,因此需提供 setter 方法,供 Dubbo 調用。

@Activate(  group = {"provider"},  value = {"customValidationFilter"},  order = 10000  
)  
@Slf4j  
public class CustomValidationFilter implements Filter {  private javax.validation.Validator validator;  // duubo會調用setter獲取beanpublic void setValidator(javax.validation.Validator validator) {  this.validator = validator;  } public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {  if (this.validator != null && !invocation.getMethodName().startsWith("$")) {  // 補充字段校驗,返回信息的組裝以及異常處理}  return invoker.invoke(invocation);  }  
}

進階使用
分組校驗
對于同一個 DTO, 不同場景下對其校驗規則可能不同, @Validted 支持按照分組分別驗證,示例代碼如下:

校驗注解的 groups 屬性中添加分組

@Data
public class NoticeDTO {@Min(value = 0L, groups = Update.class)private Long id;@NotNull(groups = {Save.class, Update.class})@Length(min = 2, max = 10, groups = {Save.class, Update.class})private String title;// 保存的時候校驗分組public interface Save {}// 更新的時候校驗分組public interface Update {}
}
@Validted 上指定分組
@PostMapping("/save")
public Response<Boolean> saveNotice(@RequestBody @Validated(NoticeDTO.Save.class) NoticeDTO noticeDTO) {// 分組為Save.class的校驗通過,執行后續邏輯return Response.ok();
}@PostMapping("/update")
public Response<Boolean> updateNotice(@RequestBody @Validated(NoticeDTO.Update.class) NoticeDTO noticeDTO) {// 分組為Update.class的校驗通過,執行后續邏輯return Response.ok();
}

自定義校驗注解
如果我們想自定義實現一些驗證邏輯,可以使用自定義注解,主要包括兩部分:實現自定義注解,實現對應的校驗器 validator。下面嘗試實現一個注解,用于校驗集合中的指定屬性是否存在重復,代碼如下:

實現校驗注解,主要需要包含 message()、 filed()、 groups() 三個方法,功能如注釋所示。

@Target({ElementType.FIELD, ElementType.PARAMETER})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
// 指定校驗器
@Constraint(validatedBy = UniqueValidator.class)  
public @interface Unique {  // 用于自定義驗證信息String message() default "字段存在重復";  // 指定集合中的待校驗字段String[] field();  // 指定分組Class<?>[] groups() default {};  
}

實現對應的校驗器,主要校驗邏輯在 isValid 方法:獲取集合中指定字段,并組裝為 set,比較 set 和集合的長度,以判斷集合中指定字段是否存在重復。

// 實現ConstraintValidator<T, R>接口,T為注解的類型,R為注解的字段類型
public class UniqueValidator implements ConstraintValidator<Unique, Collection<?>> {  private Unique unique;  @Override  public void initialize(Unique constraintAnnotation) {  this.unique = constraintAnnotation;  }  @Override  public boolean isValid(Collection collection, ConstraintValidatorContext constraintValidatorContext) {// 集合為空直接校驗通過if (collection == null || collection.size() == 0) {  return Boolean.TRUE;  }  // 從集合中獲取filed中指定的待校驗字段,看是否存在重復return Arrays.stream(unique.field())  .filter(fieldName -> fieldName != null && !"".equals(fieldName.trim()))  .allMatch(fieldName -> {// 收集集合collection中字段為fieldName的值,存入set并計算set的元素個數countint count = (int) collection.stream()  .filter(Objects::nonNull)  .map(item -> {  Class<?> clazz = item.getClass();  Field field;  try {  field = clazz.getField(fieldName);  field.setAccessible(true);  return field.get(item);  } catch (Exception e) {  return null;  }  })  .collect(Collectors.collectingAndThen(Collectors.toSet(), Set::size)); // set中元素個數count與集合長度比較,若不相等則說明collection中字段存在重復,校驗不通過if (count != collection.size()) {  return false;  }  return true;  });  }  
}

總結
通過本文我們得以了解 Spring Validation 的機理及其在實際項目中的應用。無論是標準的校驗注解,還是自定義的校驗邏輯, Spring Validation 都為開發者提供了高效且強大的校驗工具。總的來說, Spring Validation 是任何 Spring 應用不可或缺的一部分,對于追求高質量代碼的 JAVA 開發者而言,掌握其用法和最佳實踐至關重要。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/165966.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/165966.shtml
英文地址,請注明出處:http://en.pswp.cn/news/165966.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2023軟件應用類下載系統平臺源碼/手機軟件應用、新聞資訊下載站/軟件庫網站源碼

源碼簡介&#xff1a; 這個是最新軟件應用類平臺源碼、手機應用下載系統源碼、軟件應用市場下載站源碼、新聞資訊軟件下載。2023軟件應用類平臺源碼/手機軟件應用、新聞資訊下載站&#xff0c;它是軟件庫網站源碼。 最新軟件應用類平臺源碼 手機應用下載系統源碼 軟件應用市場…

NX二次開發UF_CURVE_add_faces_ocf_data 函數介紹

文章作者&#xff1a;里海 來源網站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_add_faces_ocf_data Defined in: uf_curve.h int UF_CURVE_add_faces_ocf_data(tag_t face_tag, UF_CURVE_ocf_data_p_t uf_offset_data ) overview 概述 Add a face col…

JVMj之console Java監視與管理控制臺

jconsole Java監視與管理控制臺 1、jconsole介紹 jconsole (java monitoring and management console)是一款基于JMX (Java Management Extensions) 的可視化監視和管理工具。 2、啟動jconsole 1、在linux和windwos下通過jconsole啟動即可。 2、然后會自動搜索本機運行的…

【開源】基于Vue和SpringBoot的獨居老人物資配送系統

項目編號&#xff1a; S 045 &#xff0c;文末獲取源碼。 \color{red}{項目編號&#xff1a;S045&#xff0c;文末獲取源碼。} 項目編號&#xff1a;S045&#xff0c;文末獲取源碼。 目錄 一、摘要1.1 項目介紹1.2 項目錄屏 二、功能模塊三、系統展示四、核心代碼4.1 查詢社區4…

Linux(6):文件與文件系統的壓縮,打包與備份

壓縮文件的用途與技術 由于 1 byte 8 bits &#xff0c;所以每個byte當中會有8個空格&#xff0c;而每個空格可以是0,1。 其實文件里面有相當多的『空間』存在&#xff0c;并不是完全填滿的&#xff0c;而『壓縮』的技術就是將這些『空間』填滿&#xff0c;以讓整個文件占用…

微信小程序商城實例mpvue-xbyjShop-master(附精選源碼32套,涵蓋商城團購等)

mpvue-xbyjShop 基于mpvue的微信小程序商城&#xff08;小程序端&#xff0c;服務端&#xff09; 小程序端 技術棧 mpvue mpvue-router-patch mpvue-entry vuex webpack ES6/7 flyio mpvue-wxparse 項目運行 微信開發中工具選中mpvue-xbyjShop/buyer作為項目目錄即可功…

JavaScript的學習

HTML的學習-CSDN博客 從html的學習中 其實我已經用到了 JavaScript的腳本 &#xff08;GPT&#xff09; 例如 echo <script>alert("賬號密碼錯誤"); window.location"index.html";</script>; 彈窗 然后定位到 index.html 這里能夠讓我們更…

品牌線上竄貨查的出來嗎

如果竄貨不治理會出現什么局面&#xff0c;顯見的當然是渠道混亂&#xff0c;低價叢生&#xff0c;嚴重的還會導致真假混賣&#xff0c;最后所有的后果都會由品牌承擔&#xff0c;口碑的影響是必然的&#xff0c;那品牌的衰敗也會是一種趨勢&#xff0c;所以治理竄貨是品牌發展…

C現代方法(第22章)筆記——輸入/輸出

文章目錄 第22章 輸入/輸出22.1 流22.1.1 文件指針22.1.2 標準流和重定向22.1.3 文本文件與二進制文件 22.2 文件操作22.2.1 打開文件22.2.2 模式22.2.3 關閉文件22.2.4 為打開的流附加文件22.2.5 從命令行獲取文件名22.2.6 臨時文件22.2.7 文件緩沖22.2.8 其他文件操作 22.3 格…

Windows | 模仿網易云任務欄實現自定義按鈕及縮略圖

前言 最近更新網易云發現任務欄按鈕中除了播放相關的按鈕&#xff0c;多了一個喜歡的按鈕&#xff1a; 之前我一直以為網易云任務欄的按鈕只是 Windows 為音樂軟件專門提供的&#xff0c;于是我又看了一眼系統自帶的播放器&#xff0c;發現并沒有愛心按鈕&#xff1a; 這時我就…

計算給定字符串中各個數字的和的平均值…… ← Python 列表

【題目描述】 給定字符串 s"9876543210"。 請編程計算給定字符串中各個數字的和的平均值&#xff0c;并統計大于平均值的數字個數。【算法分析】 ◆ alist("abcd") # Create a list with characters a, b, c, d◆ eval(a[i]) # Converts characters to i…

C在國內就業已經拉胯,ChatGPT告訴我的

文章目錄 一、前言二、ChatGPT查到的數據三、數據亮點 1.C語言近3年數據大跌2.招聘數量每年都在劇增的是全棧工程師3.薪資漲幅最高的是全棧和網安 四、結語 一、前言 不僅前在微信群里搭建了一個ChatGPT 5.0做智能助手&#xff0c;讓他來幫我回答群問題&#xff0c; 搭建好…

數十億美元商機!英國數字基礎設施公司Equinix與法國量子計算公司Alice Bob 合作

?&#xff08;圖片來源&#xff1a;網絡&#xff09; 近日&#xff0c;全球數字基礎設施公司Equinix宣布與全球領先的法國量子計算公司Alice & Bob合作&#xff0c;旨在共同開發市場上最為可靠的量子處理器之一。此次合作將使Equinix公司的客戶通過使用Equinix Metal和Eq…

好的程序員有什么特質呢?

程序員想要提升自己&#xff0c;一定要關注到工作中的方方面面。而一個好的程序員&#xff0c;一般都有這些特質&#xff1a; 弱者抱怨環境&#xff0c;強者改變環境 不要試圖通過抱怨環境來獲得工作環境上的改變&#xff0c;這不僅不會給你帶來任何實質性的改變&#xff0c;…

自定義字符-攝氏度漢字一

本文為博主 日月同輝&#xff0c;與我共生&#xff0c;csdn原創首發。希望看完后能對你有所幫助&#xff0c;不足之處請指正&#xff01;一起交流學習&#xff0c;共同進步&#xff01; > 發布人&#xff1a;日月同輝,與我共生_單片機-CSDN博客 > 歡迎你為獨創博主日月同…

springboot+vue項目如何集成onlyoffice開源文檔組件

一、onlyoffice是什么 ONLYOFFICE 是一個開源的辦公套件&#xff0c;適合多人在線協作。由總部位于總部在拉脫維亞的 IT 公司Acensio System SIA 開發。它提供在線協作文檔編輯器&#xff08;包括文檔、電子表格、演示文稿和表單&#xff09;&#xff0c;適用于 Windows、Linu…

python tkinter使用(五)

python tkinter使用(五) 本篇文章講述tkinter 中treeview的使用 Treeview是一個多列列表框&#xff0c;可以顯示層次數據。 #!/usr/bin/python3 # -*- coding: UTF-8 -*- """Author: zhTime 2023/11/23 下午8:28 .Email:Describe: treeview 使用 "&quo…

項目經理面試題持續更新

1.項目中常用的文檔有哪些&#xff1f; 1、可行性報告 可行性報告的目的是調查和展示任務要求&#xff0c;并確定項目是否值得和可行。可行性由五個主要因素驗證——技術和系統、成本、法律、運營和進度。次要可行性因素包括市場、資源和文化因素。 2、項目章程 項目章程是證明…

Linux上自動掛載windows下的網絡共享文件夾

比如我們想在ubuntu上掛載一個windows的共享文件夾&#xff0c;我們可以用如下方式實現。 首先我們將windows下的文件夾右鍵選擇【屬性】&#xff0c;然后選擇【共享】。 選擇需要共享的用戶&#xff0c;然后設置權限級別。 點擊共享&#xff0c;然后點擊完成。 這樣我們在wi…

Go語言網絡爬蟲工程經驗分享:pholcus庫演示抓取頭條新聞的實例

網絡爬蟲是一種自動從互聯網上獲取數據的程序&#xff0c;它可以用于各種目的&#xff0c;如數據分析、信息檢索、競爭情報等。網絡爬蟲的實現方式有很多&#xff0c;不同的編程語言和框架都有各自的優勢和特點。在本文中&#xff0c;我將介紹一種使用Go語言和pholcus庫的網絡爬…