@ExceptionHandlerの使い方と引数を徹底解説!【初心者向け】

こんにちは!今回は、Spring Frameworkで例外処理に使用されるアノテーション、@ExceptionHandlerについて詳しく解説します。@ExceptionHandlerを使えば、アプリケーション内で発生するさまざまな例外を一箇所でキャッチし、適切に処理できるようになります。


@ExceptionHandlerとは?

@ExceptionHandlerとは、

「特定の例外が発生したときに、その例外を処理するメソッドを定義する」

ことのできるアノテーションです。

アプリケーションが例外を投げた場合、通常はエラーレスポンスが返されますが、@ExceptionHandlerを使用することで、例外に応じたカスタムレスポンスを返したり、ログを記録したりすることが可能です。これにより、アプリケーションのエラーハンドリングを統一的かつ柔軟に管理できます。


基本的な使い方

まずは、基本的な@ExceptionHandlerの使い方を見てみましょう。ユーザーが存在しない場合にUserNotFoundExceptionをキャッチしてカスタムエラーメッセージを返す例を紹介します。

サーバー側のコード
@RestController
public class UserController {

    @GetMapping("/users/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.getUserById(id)
                .orElseThrow(() -> new UserNotFoundException("User not found with id: " + id));
    }

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<String> handleUserNotFound(UserNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
    }
}

この例では、/users/{id}エンドポイントでユーザーを取得し、存在しない場合はUserNotFoundExceptionを投げています。そして、@ExceptionHandlerでこの例外をキャッチし、404ステータスと共にエラーメッセージを返しています。


@ExceptionHandlerの使い方【応用編】

@ExceptionHandlerを活用すれば、さらに高度な例外処理も可能です。ここでは、少し複雑なシナリオを見ていきましょう。


1. 複数の例外を処理する

@ExceptionHandlerは複数の例外に対しても対応できます。同じハンドラで複数の例外を処理したい場合、以下のように配列で例外クラスを指定します。

@ExceptionHandler({ UserNotFoundException.class, IllegalArgumentException.class })
public ResponseEntity<String> handleMultipleExceptions(RuntimeException ex) {
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Error: " + ex.getMessage());
}

このコードでは、UserNotFoundExceptionIllegalArgumentExceptionの両方をキャッチして処理しています。どちらの例外が発生しても、400エラーレスポンスが返されます。


2. カスタム例外クラスを作る

より具体的な例外処理を行いたい場合、カスタム例外クラスを作成し、専用の@ExceptionHandlerを用意することができます。

public class CustomException extends RuntimeException {
    public CustomException(String message) {
        super(message);
    }
}

@ExceptionHandler(CustomException.class)
public ResponseEntity<String> handleCustomException(CustomException ex) {
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage());
}

このコードでは、CustomExceptionという独自の例外を定義し、発生時に500 Internal Server Errorとともにカスタムメッセージを返しています。これにより、特定の業務ロジックに応じた柔軟なエラーハンドリングが可能です。


3. 共通の例外ハンドラを作る

アプリケーション全体で同じエラーハンドリングを適用したい場合、@ControllerAdviceを使って共通の例外ハンドラを作成することができます。

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGlobalException(Exception ex) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occurred: " + ex.getMessage());
    }
}

@ControllerAdviceを使用することで、アプリケーション内のどのコントローラからでも例外が投げられた場合に、この共通のハンドラで処理されるようになります。例外が発生したときにログを残したり、共通のエラーページを表示したりするのに便利です。


4. バリデーションエラーを処理する

フォーム入力やリクエストデータのバリデーションエラーをキャッチすることも可能です。以下の例では、MethodArgumentNotValidExceptionを使ってリクエストのバリデーションエラーを処理します。

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<String> handleValidationException(MethodArgumentNotValidException ex) {
    String errorMessage = ex.getBindingResult().getAllErrors().stream()
            .map(ObjectError::getDefaultMessage)
            .collect(Collectors.joining(", "));
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Validation failed: " + errorMessage);
}

このコードでは、バリデーションエラーが発生した場合、エラーメッセージを抽出して400ステータスと共に返しています。


5. ログを記録する

エラー発生時にログを残しておくことは、トラブルシューティングの際に非常に役立ちます。以下の例では、Loggerを使って例外の詳細をログに記録しています。

private static final Logger logger = LoggerFactory.getLogger(UserController.class);

@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(Exception ex) {
    logger.error("An unexpected error occurred: ", ex);
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Something went wrong.");
}

このコードでは、例外が発生したときにエラーメッセージとスタックトレースをログに記録し、ユーザーにはシンプルなエラーメッセージを返しています。


@ExceptionHandlerの引数

@ExceptionHandlerのメソッドには、いくつかの引数を渡すことができます。

例外オブジェクト
@ExceptionHandlerのメソッドには、発生した例外オブジェクトが自動的に渡されます。これにより、例外の詳細な情報を参照したり、エラーメッセージを取得したりできます。

@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String> handleUserNotFound(UserNotFoundException ex) {
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}

HttpServletRequestやHttpServletResponse
必要に応じて、HttpServletRequestHttpServletResponseを引数として受け取ることも可能です。これにより、リクエストやレスポンスに対して直接操作を行うことができます。

@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(Exception ex, HttpServletRequest request) {
    String requestUrl = request.getRequestURI();
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body("Error occurred while accessing: " + requestUrl);
}

おわりに

@ExceptionHandlerを使えば、アプリケーション全体のエラーハンドリングを一元管理でき、コードの可読性やメンテナンス性が向上します。今回は、基本的な使い方から応用例まで紹介しましたので、ぜひ実際のプロジェクトで活用してみてください!

もっと詳しく知りたい方は、Spring公式ドキュメントを見てみてくださいね。

他の記事が見たい方はこちら!↓↓↓

タイトルとURLをコピーしました