在實際應用中,異常不是一個單一的概念,而是可以分為多種類型,每種類型的異常可能需要不同的處理邏輯。為了更好地管理和處理異常,我們可以將它們分為不同的類別,並為每個類別定義特定的處理方法。
通常,我們可以將異常分為兩大類別:業務異常和系統異常。
針對不同類型的異常,我們可以定義不同的處理邏輯,以確保我們能夠適當地應對各種情況。
以下是處理業務異常的示例程式碼:
@ExceptionHandler(BusinessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorDto handleBusinessException(BusinessException ex, HttpServletRequest request) {
    ErrorDto errorDto = new ErrorDto();
    errorDto.setTimestamp(LocalDateTime.now());
    errorDto.setStatus(HttpStatus.BAD_REQUEST.value());
    errorDto.setError("Bad Request");
    errorDto.setMessage(ex.getMessage());
    errorDto.setPath(request.getRequestURI());
    return errorDto;
}
在這個示例中,我們捕獲了一個 BusinessException,並返回一個帶有400 Bad Request狀態的錯誤DTO,其中包含有關業務異常的詳細信息。
以下是處理系統異常的示例程式碼:
@ExceptionHandler(SystemException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public void handleSystemException(SystemException ex) {
    // 無需返回具體的錯誤信息
}
在這個示例中,我們捕獲了一個 SystemException,並返回500 Internal Server Error,但不包含具體的錯誤信息。
通過將異常分為不同類型並定義相應的處理邏輯,我們可以更好地管理和處理異常,提高我們應用程式的可靠性和可維護性。這也有助於確保客戶端獲得有意義的錯誤信息,以更好地理解和處理問題。
在這個示例中,我們將創建一個簡單的 Spring Boot 應用程式,然後添加自定義異常處理,分為業務異常和系統異常兩類。
(1) 首先,讓我們創建一個 Spring Boot 應用程式:
(2) 創建一個名為 "ExceptionHandlingDemo" 的 Spring Boot 項目。
(3) 在pom.xml文件中,確保你有以下相依性:
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
(4) 接下來,我們將創建自定義的業務異常和系統異常以及相應的異常處理器。
(4.1) 首先,創建一個 BusinessException 類,代表業務異常:
public class BusinessException extends RuntimeException {
    public BusinessException(String message) {
        super(message);
    }
}
(4.2) 接著,創建一個 SystemException 類,代表系統異常:
public class SystemException extends RuntimeException {
    public SystemException(String message) {
        super(message);
    }
}
(5) 現在,我們創建一個統一異常處理器,這個處理器將處理這兩種不同類型的異常。創建一個名為 GlobalExceptionHandler 的類:
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorResponse handleBusinessException(BusinessException ex) {
        return new ErrorResponse(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
    }
    @ExceptionHandler(SystemException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public void handleSystemException(SystemException ex) {
        // 不返回具體的錯誤信息
    }
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public ErrorResponse handleOtherExceptions(Exception ex) {
        return new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Internal Server Error");
    }
}
在這個處理器中,我們使用 @ExceptionHandler 注解處理不同類型的異常。對於業務異常,我們返回一個包含錯誤信息的 ErrorResponse 物件,並將 HTTP 狀態設置為 400 Bad Request。對於系統異常,我們不返回具體的錯誤信息,只是設置 HTTP 狀態為 500 Internal Server Error。對於其他未處理的異常,我們也返回一個 ErrorResponse 物件,並設置 HTTP 狀態為 500。
(6) 最後,我們創建一個 ErrorResponse 類,用於包裝錯誤信息:
@Data // Lombok註解
@Builder // Lombok註解
public class ErrorResponse {
    private int status;
    private String message;
}
(7) 現在,我們可以在我們的應用程式中引發業務異常和系統異常,並確保它們由我們的自定義異常處理器處理。例如:
@RestController
@RequestMapping("/api")
public class BookController {
    @GetMapping("/books/{id}")
    public ResponseEntity<Book> getBook(@PathVariable Long id) {
        // 模擬業務異常
        if (id == 1) {
            throw new BusinessException("Book not found with id " + id);
        }
        // 模擬系統異常
        if (id == 2) {
            throw new SystemException("Internal Server Error");
        }
        // 正常情況下返回書籍信息
        Book book = new Book(id, "Sample Book");
        return ResponseEntity.ok(book);
    }
}
在這個示例中,我們創建了一個 BookController 並在 getBook 方法中引發了業務異常和系統異常。根據異常的類型,我們的自定義異常處理器會返回適當的 HTTP 狀態和錯誤信息。
在 Spring Boot 中,除了統一處理和返回異常信息外,日誌記錄對於診斷和追蹤應用程式中的異常情況也至關重要。 Spring Boot 集成了許多常見的日誌記錄框架,如 Logback、Log4j2 和 Java Util Logging,讓您可以輕松記錄異常信息。
以下是使用 Log4j2 作為日誌框架時,以下是如何配置和使用 Log4j2 來記錄異常信息的實踐步驟:
選擇 Log4j2 日誌框架: 確保您的 Spring Boot 項目中包含 Log4j2 的相關依賴,或者您可以在 pom.xml 文件中添加以下依賴:
<dependency>
  <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
配置 Log4j2: 在您的項目中,創建一個 Log4j2 配置文件,通常命名為 log4j2.xml。您可以將該文件放置在項目的 src/main/resources 目錄下。以下是一個簡單的 log4j2.xml 配置示例:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
  <Appenders>
      <Console name="Console" target="SYSTEM_OUT">
          <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
      </Console>
      <File name="File" fileName="logs/exception.log">
          <PatternLayout>
              <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Pattern>
          </PatternLayout>
      </File>
  </Appenders>
  <Loggers>
      <Root level="error">
          <AppenderRef ref="Console"/>
          <AppenderRef ref="File"/>
      </Root>
  </Loggers>
</Configuration>
上面的配置示例將錯誤級別(error)的日誌信息同時輸出到控制台和 logs/exception.log 文件中。
在異常處理中添加日誌記錄: 在您的自定義全局異常處理器方法中,使用 Log4j2 來記錄異常信息。例如:
@ControllerAdvice
public class GlobalExceptionHandler {
    // ...
    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorResponse handleBusinessException(BusinessException ex) {
        // 記錄業務異常
        Logger logger = LogManager.getLogger(GlobalExceptionHandler.class);
        logger.error("BusinessException: " + ex.getMessage(), ex);
        return new ErrorResponse(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
    }
    // ...
}
這裡,我們創建了一個 Logger 實例並使用 error 級別記錄異常信息。
集成監控和警報工具(可選): 根據您的項目需求,您還可以將 Log4j2 集成到監控和警報工具中,以實現更高級的異常監控和警報。
以上是使用 Log4j2 記錄異常信息的基本實踐步驟。通過選擇適當的日誌框架、配置日誌屬性、在異常處理中添加日誌記錄,您可以更好地追蹤和診斷應用程式中的異常情況,提高應用程式的可靠性和可維護性。
在這篇文章中,我們深入探討了 Spring Boot 中的統一異常處理的實踐。現在,讓我們回顧一下我們所學的主要知識點,並強調這些實踐的價值和優勢。
@ControllerAdvice 和 @ExceptionHandler 注解處理不同類型的異常情況。我們還演示了如何編寫通用的異常處理邏輯。統一異常處理和自定義錯誤頁面的實踐是開發 Web 應用程式不可或缺的一部分。它有助於提供更好的使用者體驗,確保應用程式能夠恢復正常運行,並簡化了錯誤處理程式碼的管理。
我們鼓勵您在自己的應用程式中實施良好的異常處理實踐,以提高使用者滿意度、提供更好的使用者反饋和快速解決問題。請記住,異常處理是不斷改進和調試的過程,因此要隨時準備應對新的挑戰和需求。