@ControllerAdvice による共通エラーハンドリング入門!Spring MVC初心者でもわかる例外処理の考え方
新人
「Spring MVCで画面をいくつか作ってきたんですが、エラー処理のコードがコントローラごとに増えてきて、正直どこを見ればいいのか分からなくなってきました……」
先輩
「それはよくある悩みですね。最初は @ExceptionHandler を各コントローラに書きますが、数が増えると管理が大変になります。」
新人
「全部同じようなエラー画面に遷移しているのに、同じ処理を何度も書いている気がします……」
先輩
「その状態になったら、@ControllerAdvice を使った共通エラーハンドリングを考えるタイミングです。今日はその基本を整理してみましょう。」
1. @ControllerAdvice とは何か
@ControllerAdvice とは、Spring MVCにおいて複数のコントローラに共通する処理をまとめて定義するための仕組みです。 特に「例外処理」を共通化する目的で使われることが多く、Spring MVC 共通 例外処理 初心者にとって重要な学習ポイントになります。
これまで @ExceptionHandler は、各コントローラの中に書いてきたと思います。 その場合、例外処理は「そのコントローラの中だけ」で有効になります。 @ControllerAdvice を使うと、その範囲をアプリケーション全体、または複数のコントローラに広げることができます。
言い換えると、@ControllerAdvice は「例外処理専用のコントローラのような役割」を持つ仕組みです。 通常の画面表示処理は書かず、例外が発生したときの受け皿として機能します。 そのため、@ControllerAdvice とは何かを理解することは、Spring MVCの設計を一段レベルアップさせることにつながります。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public String handleException() {
return "error";
}
}
このように、@ControllerAdvice を付けたクラスに @ExceptionHandler を定義すると、 コントローラ全体で共通の例外処理として動作します。 これが @ControllerAdvice の最も基本的な役割です。
2. なぜ共通エラーハンドリングが必要なのか
Spring MVCでアプリケーションを作り始めた直後は、コントローラの数も少なく、 @ExceptionHandler を各クラスに書いてもそれほど問題になりません。 しかし、画面や機能が増えてくると、同じような例外処理が複数の場所に散らばっていきます。
例えば、数値変換エラーや不正な入力値に対するエラー画面遷移などは、 多くのコントローラで共通して発生します。 それをすべてのコントローラに個別に書いていると、 修正が必要になったときにすべてのクラスを探して直す必要が出てきます。
これが、共通エラーハンドリングが必要になる最大の理由です。 @ControllerAdvice を使えば、例外処理を一箇所にまとめることができ、 コードの重複を防ぎ、保守性を大きく向上させることができます。
Spring MVC 共通 例外処理 初心者のうちは、 「エラーが出たら画面を切り替える」という動きだけに注目しがちですが、 実務では「どこに書くか」「どう管理するか」が非常に重要になります。 @ControllerAdvice は、その課題を解決するための仕組みです。
3. コントローラ単位の @ExceptionHandler の限界
@ExceptionHandler は、Spring MVCの例外処理を学ぶ上で最初に覚える仕組みです。 コントローラの中に書けるため、動きも分かりやすく、初心者にはとても扱いやすい方法です。
しかし、コントローラ単位の @ExceptionHandler には限界があります。 それは「同じ例外処理が何度も書かれる」という点です。 特に、共通のエラー画面を表示する場合、 どのコントローラにもほぼ同じコードが並ぶことになります。
また、例外処理が各コントローラに散らばると、 「このアプリケーションでは、どんな例外をどう扱っているのか」 という全体像が見えにくくなります。 これは、Spring MVC @ExceptionHandler に慣れてきた中級者が必ず直面する悩みです。
@Controller
public class SampleController {
@GetMapping("/sample")
public String sample(@RequestParam String value) {
int num = Integer.parseInt(value);
return "sample";
}
@ExceptionHandler(NumberFormatException.class)
public String handleNumberError() {
return "error";
}
}
このような @ExceptionHandler は、シンプルで分かりやすい反面、 同じ処理を他のコントローラでも書く必要が出てきます。 ここで「もっとまとめられないのか?」と感じたときに登場するのが、 @ControllerAdvice という仕組みです。
次のステップでは、@ControllerAdvice がどのようにして これらの問題を解決するのかを、さらに詳しく見ていくことになります。
4. @ControllerAdvice の基本的な役割と仕組み
Spring MVC ControllerAdvice を理解するためには、まず「どこにクラスを作るのか」「いつ呼ばれるのか」を 頭の中でイメージできるようになることが重要です。 @ControllerAdvice を付けたクラスは、通常の @Controller と同じように、 コントローラと同じパッケージ配下、または共通処理用のパッケージに作成します。
役割としては、画面遷移を担当するコントローラとは別に、 「例外が発生したときだけ呼ばれる特別な受け皿」と考えると分かりやすいです。 通常のリクエスト処理では一切登場せず、 コントローラの処理中に例外が発生した瞬間に Spring MVC が自動的に探しに来ます。
Spring MVC の内部では、まずリクエストを受け取ったコントローラの処理が実行されます。 その途中で例外が発生すると、 Spring MVC は「この例外を処理できる @ExceptionHandler はどこにあるか」を探します。 その際、コントローラ内だけでなく、@ControllerAdvice が付いたクラスも検索対象になります。
つまり、@ControllerAdvice は「全コントローラ共通の例外処理置き場」として機能します。 Spring MVC 共通 エラーハンドリング を行う場合、 各コントローラに散らばっていた例外処理を、 このクラス一つに集約できるのが最大の特徴です。
5. @ControllerAdvice と @ExceptionHandler の関係
@ControllerAdvice 単体では、実際の処理内容は何も定義できません。 そこで一緒に使われるのが @ExceptionHandler です。 この二つはセットで使うことで初めて意味を持ちます。
これまで学んできた @ExceptionHandler は、コントローラの中に書くことで、 そのコントローラ専用の例外処理として動作していました。 @ControllerAdvice と組み合わせると、 その @ExceptionHandler は「全コントローラ共通」の例外処理に変わります。
重要なのは、コントローラ側のコードがほとんど変わらない点です。 例外が発生する処理自体は今まで通りコントローラに書き、 例外をどう扱うかだけを外に追い出すイメージになります。 これにより、コントローラは「画面制御に集中したコード」になり、 読みやすさが大きく向上します。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NumberFormatException.class)
public String handleNumberFormatException() {
return "error";
}
}
このように定義すると、数値変換エラーがどのコントローラで発生しても、 必ずこのメソッドが呼ばれるようになります。 Spring MVC ControllerAdvice の強みは、 例外処理の流れを一箇所で把握できる点にあります。
6. 共通エラーハンドリングの基本的な実装例
ここでは、Pleiades で作成した Gradle プロジェクトを前提に、 シンプルな共通 エラーハンドリング の例を見ていきます。 特別な設定は不要で、クラスを一つ追加するだけで動作します。
まず、コントローラ側では例外処理を書かず、 通常の画面遷移処理だけを記述します。 これが @ControllerAdvice を使う最大のメリットです。
@Controller
public class SampleController {
@GetMapping("/input")
public String input(@RequestParam String value) {
int num = Integer.parseInt(value);
return "result";
}
}
次に、共通エラーハンドリング用のクラスを作成します。 このクラスは、コントローラと同じように Spring に管理され、 例外が発生したときだけ呼び出されます。
@ControllerAdvice
public class CommonErrorHandler {
@ExceptionHandler(Exception.class)
public String handleException() {
return "error";
}
}
この構成にすると、コントローラ側から例外処理のコードが完全に消えます。 どの画面で例外が発生しても、 共通のエラー画面に遷移するという動きが、 一目で分かる構成になります。
Spring MVC 共通 エラーハンドリング を導入すると、 「エラー時の画面遷移はここを見る」というルールが明確になり、 チーム開発でも非常に扱いやすくなります。
7. どのような例外を共通処理にまとめるべきか
@ControllerAdvice を使い始めると、 「すべての例外を共通処理にしてよいのか」という疑問が出てきます。 結論から言うと、すべてをまとめる必要はありません。
共通 エラーハンドリング に向いているのは、 多くの画面で共通して発生する例外です。 例えば、入力値の変換エラーや、 想定外の実行時例外などが該当します。
一方で、特定の画面だけで意味を持つ例外や、 画面ごとに表示内容を変えたい例外については、 無理に共通化しない方が分かりやすい場合もあります。 重要なのは、例外処理の置き場所を意識して設計することです。
Spring MVC ControllerAdvice を使うことで、 コントローラは本来の役割である画面制御に集中でき、 例外処理は共通クラスに集約されます。 その結果、コード全体が整理され、 初心者でも構造を理解しやすいアプリケーションになります。
共通化できる例外は積極的に @ControllerAdvice にまとめ、 そうでないものはコントローラ単位で扱う。 このバランスを意識することが、 実務で役立つ Spring MVC の設計につながります。
8. @ControllerAdvice を使うときの注意点
Spring MVC ControllerAdvice は非常に便利な仕組みですが、 使い方を誤ると、かえってアプリケーションの構造が分かりにくくなることがあります。 そのため、@ControllerAdvice 注意点 を理解したうえで導入することが重要です。
まず意識すべきなのは、@ControllerAdvice は「すべてのコントローラに影響を与える」という点です。 一つのクラスに例外処理をまとめるということは、 その処理がどの画面にも適用される可能性があるという意味になります。 例外の範囲をよく考えずに共通化すると、 本来は個別対応すべきエラーまで同じ画面に飛ばしてしまうことがあります。
また、@ExceptionHandler(Exception.class) のように、 非常に広い例外をまとめて捕まえる設計には注意が必要です。 想定していない例外まで同じ処理に流れてしまい、 原因の特定が難しくなるケースが実務ではよく発生します。 Spring MVC 例外処理 設計 では、 「どこまでを共通で扱うか」を慎重に決めることが求められます。
9. よくある設計ミスとアンチパターン
@ControllerAdvice を学び始めた直後に陥りやすいのが、 「すべての例外を一つのクラスで処理しようとする」設計です。 一見するとコードは整理されているように見えますが、 実際には例外処理の中身が肥大化し、 どの画面向けの処理なのか分からなくなってしまいます。
例えば、次のようにすべてを一つのメソッドで処理する設計は、 初心者がやりがちなアンチパターンです。
@ControllerAdvice
public class BadExampleHandler {
@ExceptionHandler(Exception.class)
public String handleAll() {
return "error";
}
}
この書き方自体は動作しますが、 例外の種類ごとの意図がコードから読み取れません。 実務では「なぜこの画面に遷移したのか」を後から追うことが重要になるため、 あまりにも大雑把な共通化は避けるべきです。
また、最初から @ControllerAdvice にすべてを任せてしまうのも設計ミスの一つです。 学習初期や小規模な画面構成では、 コントローラ単位の @ExceptionHandler の方が理解しやすく、 処理の流れを追いやすい場合も多くあります。
10. 実務での @ControllerAdvice の使われ方
実務の現場では、@ControllerAdvice は段階的に導入されることがほとんどです。 最初は各コントローラに @ExceptionHandler を書き、 共通化できる例外が明確になってから、 それらを @ControllerAdvice に移動させるという流れが一般的です。
例えば、入力値変換エラーや、 想定外の実行時例外などは、 多くの画面で同じ扱いになります。 こうした例外だけを共通クラスにまとめることで、 コントローラのコード量を減らしつつ、 例外処理の見通しを良くすることができます。
@ControllerAdvice
public class PracticalExceptionHandler {
@ExceptionHandler(NumberFormatException.class)
public String handleNumberError() {
return "inputError";
}
@ExceptionHandler(RuntimeException.class)
public String handleRuntimeError() {
return "systemError";
}
}
このように、例外の種類ごとにメソッドを分けることで、 実務でも保守しやすい Spring MVC 例外処理 設計 が実現できます。 共通化は「整理するための手段」であり、 目的ではないことを常に意識することが大切です。
11. @ExceptionHandler から @ControllerAdvice へステップアップする意味
@ExceptionHandler から @ControllerAdvice へステップアップする最大の意味は、 「例外処理を設計として考えられるようになる」点にあります。 単にエラー画面に遷移させるだけでなく、 アプリケーション全体として例外をどう扱うかを意識できるようになります。
その一方で、最初から @ControllerAdvice を使わない方がよい場合があるのも事実です。 学習初期や小規模開発では、 例外処理の流れをコントローラ内で完結させた方が理解しやすく、 デバッグもしやすいケースがあります。 無理に共通化せず、成長に合わせて導入することが重要です。
@ControllerAdvice を正しく使い始めると、 次に意識すべきテーマが自然と見えてきます。 例えば、エラー画面をどう設計するか、 例外発生時にどのようなログを残すべきか、 どこまでユーザーに情報を見せるかといった点です。
Spring MVC ControllerAdvice を学んだ先には、 エラーページ設計やログ設計といった、 より実務に近い設計の世界が待っています。 例外処理をきっかけに、 アプリケーション全体の品質を高める視点へと 学習を進めていくことが次のステップになります。