在Spring框架中,@ControllerAdvice是全局異常處理機制,用于集中處理應用程序中發生的異常。
當任何控制器方法(例如REST端點)拋出異常時,該異常會被@ControllerAdvice注解的類捕獲。
@ControllerAdvice類中的@ExceptionHandler方法用于處理特定類型的異常,并返回適當的響應。
本文我們通過一個實際場景的例子來詳細說明,在Spring Boot應用程序中處理產品相關的自定義異常并進行全局處理的情況。
步驟:
1. 創建自定義的ProductNotFoundException:
在這一步中,我們創建一個自定義異常類ProductNotFoundException,它繼承自RuntimeException。這個自定義異常用于表示系統中找不到產品的情況。通過創建自定義異常,我們可以提供更具體的錯誤信息。
public class ProductNotFoundException extends RuntimeException {
public ProductNotFoundException(Long productId) {
super(“Product not found with ID: “ + productId);
}
}
解釋:
- 自定義異常用于捕獲應用程序特定的錯誤場景。
- 在這種情況下,我們在異常消息中包含了productId,以提供有關缺失產品的詳細信息。
2. 創建產品服務(Product Service):
在這步,我們創建一個ProductService類,負責獲取產品信息。如果找不到產品,會拋出ProductNotFoundException。
@Service
public class ProductService {
public Product getProductById(Long productId) {
// 模擬獲取產品的邏輯
Product product = getProductFromDatabase(productId);
if (product == null) {
throw new ProductNotFoundException(productId);
}
return product;
}
// 模擬從數據庫獲取產品的方法
private Product getProductFromDatabase(Long productId) {
// 在這里實現您的數據庫邏輯
// 如果找不到產品,則返回null
return null;
}
}
解釋:
- ProductService封裝了與產品相關的業務邏輯。
- getProductById方法模擬從數據庫或其他數據源獲取產品。
- 如果找不到產品(基于模擬),會拋出ProductNotFoundException。
3. 創建全局異常處理器(Global Exception Handler):
這一步涉及使用@ControllerAdvice創建一個全局異常處理器。該處理器負責全局捕獲ProductNotFoundException,并返回自定義錯誤響應。
@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將該類標記為全局異常處理器,使其能夠處理來自多個控制器的異常。
- 處理器內的@ExceptionHandler方法捕獲ProductNotFoundException。
- 它使用異常中的自定義錯誤消息構建一個包含404 Not Found狀態碼的ErrorResponse對象。
- 處理器返回包含錯誤詳細信息的JSON響應。
4. 創建自定義錯誤響應類(Custom Error Response Class):
我們定義一個自定義的錯誤響應類ErrorResponse,以便在應用程序中統一結構化錯誤消息。
public class ErrorResponse {
private int statusCode;
private String message;
public ErrorResponse(int statusCode, String message) {
this.statusCode = statusCode;
this.message = message;
}
// Getter方法
}
解釋:
- ErrorResponse類提供了一個標準化的錯誤響應格式。
- 它包含HTTP狀態碼和描述錯誤的消息字段。
5. 產品控制器(Controller):
在這一步中,我們創建一個控制器ProductController,負責處理根據產品ID獲取產品的請求。
@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定義了一個端點,用于根據產品ID獲取產品詳細信息。
- 它使用ProductService來獲取產品。
- 如果找到產品,它將返回包含產品數據的成功響應。
6. 測試異常處理:
為了測試異常處理,發送GET請求來獲取在系統中不存在的產品信息。這將觸發ProductNotFoundException,并由全局異常處理器返回一個JSON格式的錯誤消息。
curl -X GET http://localhost:8080/api/products/123
響應:
{
"statusCode": 404,
"message": "Product not found with ID: 123"
}
解釋:
- 測試請求嘗試獲取一個在系統中不存在的產品的信息,例如ID為123的產品。
- 結果導致服務層拋出ProductNotFoundException異常。
- 標記為@ControllerAdvice的全局異常處理器捕獲該異常,并構建一個包含404狀態碼和自定義錯誤消息的JSON格式錯誤響應。
順序流程圖
從控制器到使用@ControllerAdvice進行錯誤處理的Spring應用程序的流程順序圖:
(1) 控制器層:
客戶端(例如Web瀏覽器或REST客戶端)向Spring應用程序發出請求,通常是向公開的HTTP端點發送請求。
(2) 控制器方法執行:
Spring MVC框架根據請求映射注解(如@GetMapping或@PostMapping)將傳入的請求路由到適當的控制器方法。
控制器方法執行并執行其業務邏輯。
(3) 異常發生:
在執行控制器方法的過程中,由于各種原因可能會拋出異常。這可能是由于業務邏輯錯誤、驗證失敗或任何其他意外問題。
4 異常傳播:
一旦在控制器方法內部拋出異常,它就會開始沿著調用堆棧向上傳播。
(55) @ControllerAdvice類:
異常向上傳播,直到達到使用@ControllerAdvice注解的全局異常處理器類為止。
(6) @ExceptionHandler方法:
在@ControllerAdvice類中,定義了一個或多個@ExceptionHandler方法來處理特定類型的異常。
(7) 匹配異常處理器:
Spring框架根據方法的參數類型識別出適當的@ExceptionHandler方法來處理特定的異常類型。
(8) 異常處理:
匹配的@ExceptionHandler方法執行以處理異常。
此方法可以執行諸如記錄錯誤、構建錯誤響應或執行任何其他自定義操作的任務。
(9) 生成響應:
@ExceptionHandler方法通常生成一個錯誤響應,可以是JSON響應、html頁面或任何其他響應格式。
(10) 響應發送給客戶端:
由@ExceptionHandler方法生成的錯誤響應發送回原始請求的客戶端。
(11) 客戶端接收錯誤響應:
客戶端接收錯誤響應,并根據需要處理錯誤信息。例如,它可以向用戶顯示錯誤消息或以編程方式處理錯誤。