
こんにちは!今回は、Spring Frameworkの例外処理やコントローラ横断的なロジックを実装するためのアノテーション、@ControllerAdviceについて詳しく解説します。@ControllerAdviceを使うことで、アプリケーション全体で共通の処理を簡単に管理できるようになります。
@ControllerAdviceとは?
@ControllerAdviceとは、
「全てのコントローラに適用される横断的な処理を定義できるアノテーション」
のことです。
例えば、アプリ全体で共通のエラーハンドリングをしたい場合、@ControllerAdviceを使うと一箇所でエラー処理をまとめて設定できます。
これにより、各コントローラに同じエラーハンドリングを書く必要がなくなり、コードがスッキリします。
主に次のような場面で活用されます。
- 共通の例外処理
 - 共通のバリデーションエラー処理
 - 横断的なデータバインディングや事前処理
 
基本的な使い方
まずは、基本的なコード例を見てみましょう。ここでは、エラーハンドリングの例を紹介します。
サーバー側のコード
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(value = { IllegalArgumentException.class })
    public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException e) {
        return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
    }
}このコードでは、アプリケーション内で発生したIllegalArgumentExceptionをキャッチし、適切なエラーメッセージとステータスコード(400 Bad Request)を返しています。@ExceptionHandlerアノテーションを使うことで、特定の例外に対応するハンドラメソッドを簡単に作成できます。
クライアント側のコード(例)
fetch("http://localhost:8080/api/resource", {
    method: 'GET',
})
.then(response => {
    if (!response.ok) {
        throw new Error('Bad Request');
    }
    return response.json();
})
.catch(error => {
    console.error('Error:', error);
});クライアント側では、エラーレスポンスが返された際にエラーメッセージを表示する例です。
@ControllerAdviceの使い方【応用編】
@ControllerAdviceは、さまざまな場面で応用できます。ここからは、さらに複雑なケースを見ていきましょう。
1. 特定のコントローラだけに適用する
デフォルトでは、@ControllerAdviceは全てのコントローラに適用されますが、特定のコントローラにのみ適用することも可能です。
@ControllerAdvice(assignableTypes = { UserController.class })
public class UserControllerAdvice {
    @ExceptionHandler(value = { NullPointerException.class })
    public ResponseEntity<String> handleNullPointerException(NullPointerException e) {
        return new ResponseEntity<>("User not found", HttpStatus.NOT_FOUND);
    }
}この例では、UserControllerクラスに対してのみ例外処理が適用されるようにしています。assignableTypes属性で、適用するコントローラを指定できます。
2. 全ての例外を一括で処理
複数の例外を一括で処理したい場合は、共通の例外クラスを利用するか、例外ハンドラメソッドをまとめて定義できます。
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(value = { Exception.class })
    public ResponseEntity<String> handleAllExceptions(Exception e) {
        return new ResponseEntity<>("An error occurred: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}この例では、Exceptionクラスを捕捉して、アプリケーション内で発生する全ての例外を一括で処理しています。
3. カスタム例外の処理
自分で定義したカスタム例外を処理することも可能です。たとえば、次のようにカスタム例外クラスを作成し、それに対応するハンドラを定義します。
public class CustomException extends RuntimeException {
    public CustomException(String message) {
        super(message);
    }
}
@ControllerAdvice
public class CustomExceptionHandler {
    @ExceptionHandler(value = { CustomException.class })
    public ResponseEntity<String> handleCustomException(CustomException e) {
        return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
    }
}カスタム例外が発生した場合、このハンドラが呼び出され、適切なレスポンスが返されます。
4. バリデーションエラーの処理
フォームデータやAPIリクエストのバリデーションエラーも、@ControllerAdviceを使って一括で処理することができます。
@ControllerAdvice
public class ValidationExceptionHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(error -> {
            errors.put(error.getField(), error.getDefaultMessage());
        });
        return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
    }
}この例では、リクエストのバリデーションエラーをキャッチして、フィールドごとのエラーメッセージをクライアントに返します。
5. JSONレスポンスを返す
例外が発生した際、エラーメッセージをJSON形式で返すことも可能です。次のコードでは、カスタムのエラーレスポンスをJSONで返しています。
@ControllerAdvice
public class JsonExceptionHandler {
    @ExceptionHandler(value = { IllegalArgumentException.class })
    public ResponseEntity<Map<String, String>> handleIllegalArgumentException(IllegalArgumentException e) {
        Map<String, String> errorResponse = new HashMap<>();
        errorResponse.put("error", e.getMessage());
        return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
    }
}この方法により、クライアント側でのエラーハンドリングが簡単になります。
6. HTTPヘッダーに基づいたエラーハンドリング
認証情報やその他のヘッダーに基づいてエラーハンドリングを行うこともできます。
@ControllerAdvice
public class HeaderExceptionHandler {
    @ExceptionHandler(value = { IllegalAccessException.class })
    public ResponseEntity<String> handleIllegalAccessException(IllegalAccessException e, @RequestHeader("Authorization") String token) {
        if (token == null || !authService.isValidToken(token)) {
            return new ResponseEntity<>("Invalid token", HttpStatus.UNAUTHORIZED);
        }
        return new ResponseEntity<>("Access denied: " + e.getMessage(), HttpStatus.FORBIDDEN);
    }
}この例では、認証トークンが無効な場合に401 Unauthorizedを返し、その他のアクセスエラーには403 Forbiddenを返します。
@ControllerAdviceの引数
@ControllerAdviceでは、さまざまな引数を使って処理を細かく制御できます。
1. assignableTypes属性
assignableTypes属性を使うことで、特定のコントローラに対してのみ適用することが可能です。
@ControllerAdvice(assignableTypes = { UserController.class })
2. basePackages属性
basePackages属性を使用して、特定のパッケージのコントローラに対してだけ適用することができます。
@ControllerAdvice(basePackages = "com.example.controllers")
おわりに
@ControllerAdviceを使えば、Springアプリケーション全体で例外処理や共通ロジックを簡単に実装できます。特にエラーハンドリングにおいて、コードの再利用性や可読性が向上し、アプリケーションの品質が高まります。ぜひ、自分のプロジェクトでも活用してみてください!
もっと詳しく知りたい方は、Spring公式ドキュメントを見てみてくださいね。
他の記事が見たい方はこちら!↓↓↓
  
  
  
  