Spring?Assert?使用全面指南
在Spring框架中,Assert類主要用于在開(kāi)發(fā)過(guò)程中對(duì)代碼進(jìn)行驗(yàn)證,確保程序的正確性和穩(wěn)定性。Assert類屬于org.springframework.util包。它提供了一系列靜態(tài)方法,用于在運(yùn)行時(shí)檢測(cè)條件是否滿足,如果不滿足則拋出IllegalArgumentException異常。這對(duì)于參數(shù)校驗(yàn)、狀態(tài)檢查等場(chǎng)景非常有用。
Assert
寫(xiě)在前面
這篇文檔原本并不在我的計(jì)劃之內(nèi)。我原本打算先完成 Spring AI 框架篇的內(nèi)容,但那一部分的文檔進(jìn)展不太順利,暫時(shí)難以成文。最近在研究 Spring AI 框架源碼的過(guò)程中,我發(fā)現(xiàn) Spring 團(tuán)隊(duì)大量使用了 Assert 類來(lái)校驗(yàn)方法參數(shù)的規(guī)范性。深入研究之后,我發(fā)現(xiàn)這個(gè)類非常實(shí)用,并且它與 org.junit 包下的 Assert 類有很大區(qū)別。本文旨在說(shuō)明 org.springframework.util 包下的 Assert 類的作用、它與 org.junit 包下的 Assert 有何不同,以及它如何幫助我們簡(jiǎn)化日常開(kāi)發(fā),使代碼更加優(yōu)雅。
一、Assert是什么
在 Spring 框架中,Assert 是 org.springframework.util 包下的一個(gè)工具類,它通過(guò)一系列靜態(tài)方法實(shí)現(xiàn)對(duì)方法參數(shù)、對(duì)象狀態(tài)或業(yè)務(wù)邏輯前提的校驗(yàn)。其核心設(shè)計(jì)思想是:在程序運(yùn)行時(shí)主動(dòng)檢查關(guān)鍵條件是否滿足,若不滿足則立即拋出異常,快速暴露問(wèn)題,避免錯(cuò)誤在后續(xù)流程中被掩蓋或放大。
簡(jiǎn)單來(lái)說(shuō),Assert 的作用可以概括為 “條件校驗(yàn) + 異常拋出的自動(dòng)化封裝”,僅專注于 “校驗(yàn)” 這一單一職責(zé)。它替代了傳統(tǒng)開(kāi)發(fā)中 “if (!條件) { throw new 異常(...); }” 這種重復(fù)的校驗(yàn)邏輯,用更簡(jiǎn)潔的代碼表達(dá)校驗(yàn)意圖,讓開(kāi)發(fā)者能更專注于核心業(yè)務(wù)邏輯。
二、Assert怎么用
接下來(lái)讓我們來(lái)舉個(gè)例子說(shuō)明這一點(diǎn),在不使用 Assert 之前,我們大多數(shù)業(yè)務(wù)代碼中的數(shù)據(jù)校驗(yàn)是這樣做的
List<ChatMemory> list = chatMemoryMapper.selectBySessionId("111");
if (list == null || list.isEmpty()){
throw new RuntimeException("會(huì)話不存在");
}
//下面處理會(huì)話相關(guān)邏輯
判斷一個(gè)數(shù)據(jù)是否存在,如果存在繼續(xù)下面的業(yè)務(wù)代碼,如果不存在則拋出異常讓外層捕獲處理
使用Assert來(lái)進(jìn)行處理是這樣子的
List<ChatMemory> list = chatMemoryMapper.selectBySessionId("111");
Assert.notEmpty(list, "會(huì)話不存在");
//下面處理會(huì)話相關(guān)邏輯
Assert.notEmpty()方法的實(shí)現(xiàn)如下
@Contract("null, _ -> fail")
public static void notEmpty(@Nullable Collection<?> collection, String message) {
if (CollectionUtils.isEmpty(collection)) {
throw new IllegalArgumentException(message);
}
}
Assert 類本身不包含任何業(yè)務(wù)邏輯,僅專注于 “校驗(yàn)” 這一單一職責(zé)。它的所有方法均為靜態(tài)方法,無(wú)需實(shí)例化即可直接調(diào)用(如 Assert.notNull(obj, "對(duì)象不能為空")),且校驗(yàn)失敗時(shí)會(huì)拋出 IllegalArgumentException(參數(shù)不合法)或 IllegalStateException(狀態(tài)不合法)等運(yùn)行時(shí)異常。
雖然拋出異常需要處理,但也不應(yīng)在所有業(yè)務(wù)接口中都重復(fù)編寫(xiě) try-catch 代碼,否則會(huì)降低代碼的可讀性并導(dǎo)致冗余??蓪?code>Assert與 Spring MVC 的全局異常處理器結(jié)合使用
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 處理Assert拋出的IllegalArgumentException異常
*/
@ExceptionHandler(IllegalArgumentException.class)
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, Object> handleIllegalArgumentException(IllegalArgumentException e) {
Map<String, Object> result = new HashMap<>();
result.put("code", 400);
result.put("message", e.getMessage());
// 可以根據(jù)需要添加更多信息,如時(shí)間戳、請(qǐng)求路徑等
return result;
}
/**
* 處理Assert拋出的IllegalStateException異常
*/
@ExceptionHandler(IllegalStateException.class)
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Map<String, Object> handleIllegalStateException(IllegalStateException e) {
Map<String, Object> result = new HashMap<>();
result.put("code", 500);
result.put("message", e.getMessage());
return result;
}
}當(dāng) Assert 校驗(yàn)失敗拋出異常(如 IllegalArgumentException)時(shí),全局異常處理器會(huì)捕獲并處理這些異常。這種做法的優(yōu)勢(shì)在于:
- 職責(zé)分離:業(yè)務(wù)代碼只關(guān)注參數(shù)校驗(yàn)邏輯,異常處理邏輯集中在全局異常處理器中
- 代碼簡(jiǎn)潔:避免了大量重復(fù)的
if-else和try-catch代碼 - 統(tǒng)一格式:所有異常響應(yīng)都遵循統(tǒng)一格式,便于前端處理
- 易于維護(hù):異常處理邏輯集中管理,修改時(shí)只需調(diào)整一處
通過(guò)這種方式,我們既利用了 Assert 的簡(jiǎn)潔校驗(yàn)?zāi)芰Γ滞ㄟ^(guò)全局異常處理器實(shí)現(xiàn)了異常的統(tǒng)一管理,讓代碼更加優(yōu)雅、易讀和易維護(hù)。
三、Assert有哪些方法
| 分類 | 方法簽名 | 作用描述 | 參數(shù)說(shuō)明 | 拋出異常 | 示例代碼 |
|---|---|---|---|---|---|
| 對(duì)象校驗(yàn) | notNull(Object object, String message) | 檢查對(duì)象不為 null,若為 null 則失敗 | object:待校驗(yàn)對(duì)象;message:失敗提示信息 | IllegalArgumentException | Assert.notNull(user, "用戶對(duì)象不能為空"); |
isNull(Object object, String message) | 檢查對(duì)象必須為 null,若不為 null 則失?。ㄌ厥鈭?chǎng)景用) | 同 notNull | IllegalArgumentException | Assert.isNull(deletedUser, "已刪除用戶應(yīng)不存在"); | |
| 集合 / 數(shù)組校驗(yàn) | notEmpty(Collection<?> collection, String message) | 檢查集合不為 null 且非空(size > 0) | collection:待校驗(yàn)集合;message:失敗提示信息 | IllegalArgumentException | Assert.notEmpty(userList, "用戶列表不能為空"); |
notEmpty(Object[] array, String message) | 檢查數(shù)組不為 null 且非空(length > 0) | array:待校驗(yàn)數(shù)組;message:失敗提示信息 | IllegalArgumentException | Assert.notEmpty(ids, "ID數(shù)組不能為空"); | |
noNullElements(Object[] array, String message) | 檢查數(shù)組中無(wú) null 元素(數(shù)組本身可空,但非空元素不能為 null) | 同數(shù)組 notEmpty | IllegalArgumentException | Assert.noNullElements(names, "名稱數(shù)組不可含null"); | |
| 字符串校驗(yàn) | hasLength(String text, String message) | 檢查字符串不為 null 且長(zhǎng)度 > 0(允許全空白,如 " ") | text:待校驗(yàn)字符串;message:失敗提示信息 | IllegalArgumentException | Assert.hasLength(username, "用戶名長(zhǎng)度不能為0"); |
hasText(String text, String message) | 檢查字符串不為 null 且含非空白字符(trim().length > 0,排除全空白) | 同 hasLength | IllegalArgumentException | Assert.hasText(username, "用戶名不能為空白"); | |
doesNotContain(String textToSearch, String substring, String message) | 檢查字符串 textToSearch 中不包含子串 substring | textToSearch:被檢查字符串;substring:禁止出現(xiàn)的子串;message:提示信息 | IllegalArgumentException | Assert.doesNotContain(username, "admin", "用戶名不可含'admin'"); | |
| 條件校驗(yàn) | isTrue(boolean condition, String message) | 檢查布爾條件為 true,若為 false 則失?。ㄍㄓ脳l件校驗(yàn)) | condition:待校驗(yàn)條件;message:失敗提示信息 | IllegalArgumentException | Assert.isTrue(age >= 18, "年齡必須≥18歲"); |
state(boolean condition, String message) | 檢查對(duì)象狀態(tài)為 true(語(yǔ)義上用于狀態(tài)檢查,非參數(shù)校驗(yàn)) | 同 isTrue | IllegalStateException | Assert.state(order.isPaid(), "訂單未支付,不能發(fā)貨"); | |
| 類型校驗(yàn) | isInstanceOf(Class<?> type, Object obj, String message) | 檢查 obj 是 type 或其子類的實(shí)例 | type:預(yù)期類型;obj:待校驗(yàn)對(duì)象;message:提示信息 | IllegalArgumentException | Assert.isInstanceOf(User.class, obj, "對(duì)象必須是User類型"); |
isAssignable(Class<?> superType, Class<?> subType, String message) | 檢查 subType 可賦值給 superType(即 superType 是 subType 的父類) | superType:父類型;subType:子類型;message:提示信息 | IllegalArgumentException | Assert.isAssignable(Number.class, Integer.class, "Integer必須是Number子類"); |
四、Assert與Assert的區(qū)別
org.springframework.util.Assert 與 org.junit.Assert 的最大區(qū)別一個(gè)拋出RuntimeException 另一個(gè)拋出 Error。
從設(shè)計(jì)角度看 org.springframework.util.Assert 適合用于生產(chǎn)代碼中的參數(shù)校驗(yàn)、狀態(tài)檢查(業(yè)務(wù)邏輯層的前提校驗(yàn))
而 org.junit.Assert 適合用于測(cè)試代碼中的結(jié)果斷言(驗(yàn)證測(cè)試用例的預(yù)期結(jié)果)
到此這篇關(guān)于Spring Assert 使用全面指南的文章就介紹到這了,更多相關(guān)Spring Assert 使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java設(shè)置請(qǐng)求響應(yīng)時(shí)間的多種實(shí)現(xiàn)方式
在前后端分離的開(kāi)發(fā)模式中,前端請(qǐng)求后端獲取數(shù)據(jù)時(shí),合理設(shè)置響應(yīng)時(shí)間(超時(shí)時(shí)間)是提升系統(tǒng)性能和用戶體驗(yàn)的關(guān)鍵,本文將深入探討如何在Java中設(shè)置請(qǐng)求的響應(yīng)時(shí)間,需要的朋友可以參考下2025-01-01
Java并發(fā)編程之如何優(yōu)雅關(guān)閉鉤子Shutdown Hook
這篇文章主要為大家詳細(xì)介紹了Java如何實(shí)現(xiàn)優(yōu)雅關(guān)閉鉤子Shutdown Hook,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-04-04
java mybatis框架實(shí)現(xiàn)多表關(guān)系查詢功能
這篇文章主要介紹了java mybatis框架實(shí)現(xiàn)多表關(guān)系查詢,基于Maven框架的整體設(shè)計(jì) —— 一多一的關(guān)系,文中通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10
Spring?boot事務(wù)無(wú)效報(bào)錯(cuò):Transaction?not?enabled問(wèn)題排查解決
在業(yè)務(wù)代碼中經(jīng)常需要保證事務(wù)的原子性,但是有的時(shí)候確實(shí)是出現(xiàn)事務(wù)沒(méi)有生效,這篇文章主要給大家介紹了關(guān)于Spring?boot事務(wù)無(wú)效報(bào)錯(cuò):Transaction?not?enabled問(wèn)題排查的相關(guān)資料,需要的朋友可以參考下2023-11-11

