六個(gè)Spring Boot處理異常的小技巧
在Spring框架中,@ControllerAdvice是全局異常處理機(jī)制,用于集中處理應(yīng)用程序中發(fā)生的異常。
當(dāng)任何控制器方法(例如REST端點(diǎn))拋出異常時(shí),該異常會(huì)被@ControllerAdvice注解的類捕獲。
@ControllerAdvice類中的@ExceptionHandler方法用于處理特定類型的異常,并返回適當(dāng)?shù)捻憫?yīng)。
本文我們通過一個(gè)實(shí)際場景的例子來詳細(xì)說明,在Spring Boot應(yīng)用程序中處理產(chǎn)品相關(guān)的自定義異常并進(jìn)行全局處理的情況。
步驟:
1. 創(chuàng)建自定義的ProductNotFoundException:
在這一步中,我們創(chuàng)建一個(gè)自定義異常類ProductNotFoundException,它繼承自RuntimeException。這個(gè)自定義異常用于表示系統(tǒng)中找不到產(chǎn)品的情況。通過創(chuàng)建自定義異常,我們可以提供更具體的錯(cuò)誤信息。
public class ProductNotFoundException extends RuntimeException {
public ProductNotFoundException(Long productId) {
super(“Product not found with ID: “ + productId);
}
}
解釋:
- 自定義異常用于捕獲應(yīng)用程序特定的錯(cuò)誤場景。
- 在這種情況下,我們?cè)诋惓O⒅邪藀roductId,以提供有關(guān)缺失產(chǎn)品的詳細(xì)信息。
2. 創(chuàng)建產(chǎn)品服務(wù)(Product Service):
在這步,我們創(chuàng)建一個(gè)ProductService類,負(fù)責(zé)獲取產(chǎn)品信息。如果找不到產(chǎn)品,會(huì)拋出ProductNotFoundException。
@Service
public class ProductService {
public Product getProductById(Long productId) {
// 模擬獲取產(chǎn)品的邏輯
Product product = getProductFromDatabase(productId);
if (product == null) {
throw new ProductNotFoundException(productId);
}
return product;
}
// 模擬從數(shù)據(jù)庫獲取產(chǎn)品的方法
private Product getProductFromDatabase(Long productId) {
// 在這里實(shí)現(xiàn)您的數(shù)據(jù)庫邏輯
// 如果找不到產(chǎn)品,則返回null
return null;
}
}
解釋:
- ProductService封裝了與產(chǎn)品相關(guān)的業(yè)務(wù)邏輯。
- getProductById方法模擬從數(shù)據(jù)庫或其他數(shù)據(jù)源獲取產(chǎn)品。
- 如果找不到產(chǎn)品(基于模擬),會(huì)拋出ProductNotFoundException。
3. 創(chuàng)建全局異常處理器(Global Exception Handler):
這一步涉及使用@ControllerAdvice創(chuàng)建一個(gè)全局異常處理器。該處理器負(fù)責(zé)全局捕獲ProductNotFoundException,并返回自定義錯(cuò)誤響應(yīng)。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ProductNotFoundException.class)
public ResponseEntity<ErrorResponse> handleProductNotFoundException(ProductNotFoundException ex) {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
}
}
解釋:
- @ControllerAdvice將該類標(biāo)記為全局異常處理器,使其能夠處理來自多個(gè)控制器的異常。
- 處理器內(nèi)的@ExceptionHandler方法捕獲ProductNotFoundException。
- 它使用異常中的自定義錯(cuò)誤消息構(gòu)建一個(gè)包含404 Not Found狀態(tài)碼的ErrorResponse對(duì)象。
- 處理器返回包含錯(cuò)誤詳細(xì)信息的JSON響應(yīng)。
4. 創(chuàng)建自定義錯(cuò)誤響應(yīng)類(Custom Error Response Class):
我們定義一個(gè)自定義的錯(cuò)誤響應(yīng)類ErrorResponse,以便在應(yīng)用程序中統(tǒng)一結(jié)構(gòu)化錯(cuò)誤消息。
public class ErrorResponse {
private int statusCode;
private String message;
public ErrorResponse(int statusCode, String message) {
this.statusCode = statusCode;
this.message = message;
}
// Getter方法
}
解釋:
- ErrorResponse類提供了一個(gè)標(biāo)準(zhǔn)化的錯(cuò)誤響應(yīng)格式。
- 它包含HTTP狀態(tài)碼和描述錯(cuò)誤的消息字段。
5. 產(chǎn)品控制器(Controller):
在這一步中,我們創(chuàng)建一個(gè)控制器ProductController,負(fù)責(zé)處理根據(jù)產(chǎn)品ID獲取產(chǎn)品的請(qǐng)求。
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("/{productId}")
public ResponseEntity<Product> getProduct(@PathVariable Long productId) {
Product product = productService.getProductById(productId);
return ResponseEntity.ok(product);
}
}
解釋:
- ProductController定義了一個(gè)端點(diǎn),用于根據(jù)產(chǎn)品ID獲取產(chǎn)品詳細(xì)信息。
- 它使用ProductService來獲取產(chǎn)品。
- 如果找到產(chǎn)品,它將返回包含產(chǎn)品數(shù)據(jù)的成功響應(yīng)。
6. 測試異常處理:
為了測試異常處理,發(fā)送GET請(qǐng)求來獲取在系統(tǒng)中不存在的產(chǎn)品信息。這將觸發(fā)ProductNotFoundException,并由全局異常處理器返回一個(gè)JSON格式的錯(cuò)誤消息。
curl -X GET http://localhost:8080/api/products/123
響應(yīng):
{
"statusCode": 404,
"message": "Product not found with ID: 123"
}
解釋:
- 測試請(qǐng)求嘗試獲取一個(gè)在系統(tǒng)中不存在的產(chǎn)品的信息,例如ID為123的產(chǎn)品。
- 結(jié)果導(dǎo)致服務(wù)層拋出ProductNotFoundException異常。
- 標(biāo)記為@ControllerAdvice的全局異常處理器捕獲該異常,并構(gòu)建一個(gè)包含404狀態(tài)碼和自定義錯(cuò)誤消息的JSON格式錯(cuò)誤響應(yīng)。
順序流程圖
從控制器到使用@ControllerAdvice進(jìn)行錯(cuò)誤處理的Spring應(yīng)用程序的流程順序圖:
(1) 控制器層:
客戶端(例如Web瀏覽器或REST客戶端)向Spring應(yīng)用程序發(fā)出請(qǐng)求,通常是向公開的HTTP端點(diǎn)發(fā)送請(qǐng)求。
(2) 控制器方法執(zhí)行:
Spring MVC框架根據(jù)請(qǐng)求映射注解(如@GetMapping或@PostMapping)將傳入的請(qǐng)求路由到適當(dāng)?shù)目刂破鞣椒ā?/p>
控制器方法執(zhí)行并執(zhí)行其業(yè)務(wù)邏輯。
(3) 異常發(fā)生:
在執(zhí)行控制器方法的過程中,由于各種原因可能會(huì)拋出異常。這可能是由于業(yè)務(wù)邏輯錯(cuò)誤、驗(yàn)證失敗或任何其他意外問題。
4 異常傳播:
一旦在控制器方法內(nèi)部拋出異常,它就會(huì)開始沿著調(diào)用堆棧向上傳播。
(55) @ControllerAdvice類:
異常向上傳播,直到達(dá)到使用@ControllerAdvice注解的全局異常處理器類為止。
(6) @ExceptionHandler方法:
在@ControllerAdvice類中,定義了一個(gè)或多個(gè)@ExceptionHandler方法來處理特定類型的異常。
(7) 匹配異常處理器:
Spring框架根據(jù)方法的參數(shù)類型識(shí)別出適當(dāng)?shù)腀ExceptionHandler方法來處理特定的異常類型。
(8) 異常處理:
匹配的@ExceptionHandler方法執(zhí)行以處理異常。
此方法可以執(zhí)行諸如記錄錯(cuò)誤、構(gòu)建錯(cuò)誤響應(yīng)或執(zhí)行任何其他自定義操作的任務(wù)。
(9) 生成響應(yīng):
@ExceptionHandler方法通常生成一個(gè)錯(cuò)誤響應(yīng),可以是JSON響應(yīng)、HTML頁面或任何其他響應(yīng)格式。
(10) 響應(yīng)發(fā)送給客戶端:
由@ExceptionHandler方法生成的錯(cuò)誤響應(yīng)發(fā)送回原始請(qǐng)求的客戶端。
(11) 客戶端接收錯(cuò)誤響應(yīng):
客戶端接收錯(cuò)誤響應(yīng),并根據(jù)需要處理錯(cuò)誤信息。例如,它可以向用戶顯示錯(cuò)誤消息或以編程方式處理錯(cuò)誤。