---
# System prepended metadata

title: Java Spring Boot｜用 @ExceptionHandler 進行全局例外處理
tags: [Java, Spring Boot, 學習筆記]

---

# Java Spring Boot｜用 @ExceptionHandler 進行全局例外處理


## 動機
在 Spring Boot 中，例外可能遍佈整個專案，在沒有全域處理的情況下，開發者必須在 Service 層與 Controller 層之間建立一套複雜的管理機制，例如 controller 可能如下：

```java
@GetMapping("/{id}")
public ResponseEntity<> get(@PathVariable int id) {
    try {
        User user = service.find(id);
        return ResponseEntity.ok(user); 
        
    } catch (BusinessException e) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
            .body("Business error: " + e.getMessage());
                             
    } catch (NotExistError e) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
            .body("Data not exist.");
                             
    } catch (Exception e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body("An unexpected error occurred.");
    }
}
```
上面使用 try-catch 的方法在 controller 層當中進行判斷，會造成代碼冗長以及管理和維護困難，因此使用全域異常處理 (Global Exception Handling) 來管理例外處理，可以讓專案結構更加整潔易讀。

## 核心概念
利用 Java 的 RuntimeException 特性與 Spring 的全域攔截，我們可以達成：
- Service 層：發現錯誤直接丟出，例外會穿透所有中間層級，調用此 service 的所有中間層級都不需要寫任何接收代碼或在方法宣告 throws。
- Controller 層：專注於回傳正確的資料。
## 實例

首先建立一個全局處理類別；
```java
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<String> handleBusiness(BusinessException e) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
            .body(e.getMessage());
    }

    @ExceptionHandler(NotExistError.class)
    public ResponseEntity<String> handleNotFound(NotExistError e) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
            .body("Data not exist.");
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGeneral(Exception e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body("An unexpected error occurred.");
    }
}
```

假設 service 層有以下的例外：

```java
public User find(int id) {
    User user = repository.findById(id);

    if (user == null) throw new NotExistError(); 
    if (user.isLocked()) throw new BusinessException("User account is locked.");
    
    return user;
}
```
有了全局例外處理器，就算 service 層有丟出例外，controller 也不需要進行處理。這些例外在 service 被拋出之後，會直接被全局例外處理器接住。

更新後的 controller 可以變得非常簡潔：
```java
@GetMapping("/{id}")
public ResponseEntity<User> get(@PathVariable int id) {
    User user = service.find(id);
    return ResponseEntity.ok(user);
}
```
