Spring MVC: リクエストがControllerに届くまで
経緯
Spring Framework を使っていてふと「リクエストを受けてから、どうやって Controller のメソッドが実行されるんだろう?」という疑問が湧いたので、その仕組みを調べた記録です。
1. リクエストの入り口: DispatcherServlet の役割
Spring MVC アプリケーションに HTTP リクエストが届くと、まず DispatcherServlet というサーブレットが受け取ります。DispatcherServlet は Spring MVC の「司令塔」とも言える存在で、すべてのリクエスト処理の中心となります。
その役割は大きく分けて以下の通りです。
- リクエストの受付: Web サーバー(Tomcat など)が受け取った HTTP リクエストを最初に処理します。
- 適切な担当者の特定: リクエストの内容(URL や HTTP メソッドなど)に応じて、どの Controller のどのメソッドが処理すべきかを特定します。
- Controller の実行: 特定した Controller を実際に呼び出し、ビジネスロジックを実行させます。
- 結果の処理: Controller の実行結果(表示する画面や返すデータなど)を受け取り、クライアントに返すレスポンスを生成します。
2. リクエストのルーティング: HandlerMapping
DispatcherServlet は、リクエストを受け取ると、まず HandlerMapping を使って、そのリクエストを処理すべき Controller (正確には、ハンドラーオブジェクト) を見つけ出します。
-
何をするの?
リクエストの URL や HTTP メソッド(GET, POST など)を見て、「このリクエストは、あの Controller のこのメソッドに任せよう」とマッピング(紐付け)します。 -
どうやって見つけるの?
Spring Boot のような現代の Spring アプリケーションでは、主に@Controllerや@RestControllerクラスに付与された@RequestMapping、@GetMapping、@PostMappingなどのアノテーションを解析してマッピングを行います。この役割を担うのが、RequestMappingHandlerMappingというHandlerMappingの実装クラスです。
HandlerMapping は、リクエストを処理するハンドラーを見つけるだけでなく、そのハンドラーの実行前後に適用されるべき インターセプター(HandlerInterceptor)のリストも一緒に DispatcherServlet に返します。これを HandlerExecutionChain と呼びます。
3. Controller の実行と結果の処理: HandlerAdapter とその連携
HandlerMapping がリクエストを処理する Controller(ハンドラーオブジェクト)を見つけたら、DispatcherServlet は次に、そのハンドラーを実際に呼び出すための「通訳者」を探します。それが HandlerAdapter です。
HandlerAdapter の役割
HandlerAdapter は、Spring MVC に様々な Controller の形式(例えば @RequestMapping アノテーションを使うものや、古い Controller インターフェースを実装するものなど)が存在する中で、DispatcherServlet がどの形式の Controller でも統一的に呼び出せるようにする役割を担います。これにより、DispatcherServlet は特定の Controller の実装詳細に依存せず、柔軟に Controller を実行できます。
連携の仕組み
-
HandlerAdapterの準備:
DispatcherServletは起動時、Spring の IoC コンテナ (ApplicationContext) から、必要なすべてのHandlerAdapterのインスタンスを取得し、リストとして内部に保持します。これにより、DispatcherServletは具体的なHandlerAdapterの実装を知らずに、コンテナが提供するアダプターを利用できます。代表的なHandlerAdapterには、@RequestMapping付きメソッドを扱うRequestMappingHandlerAdapterや、古いControllerインターフェースを扱うSimpleControllerHandlerAdapterなどがあります。 -
実行時の選択:
リクエストが来てハンドラーオブジェクトが特定されると、DispatcherServletは持っているHandlerAdapterのリストを順に確認します。それぞれのHandlerAdapterに 「このハンドラーを処理できますか?」(supports()メソッド) と問い合わせ、最初に「はい、できます!」と答えたHandlerAdapterが選ばれます。 -
Controller メソッドの実行:
選ばれたHandlerAdapterが、特定された Controller のメソッドを実際に呼び出し、ビジネスロジックを実行させます。この際、リクエストパラメータの解決(例:@RequestParamや@PathVariableなど)も行われます。 -
結果の処理:
Controller から返された結果(例えばModelAndViewオブジェクトや、直接返すデータなど)はDispatcherServletに渡されます。-
DispatcherServletは、もし結果が論理的なビュー名(例: "home")であれば、ViewResolverを使って、そのビュー名から実際のビューオブジェクト(例:home.jsp)を見つけ出し、モデルデータと共にレンダリング(HTMLなどを生成)します。 - Controller が直接データを返す場合(例:
@ResponseBodyアノテーションを使用している場合)、そのデータが HTTP レスポンスのボディに直接書き込まれます。
-
-
レスポンスの返却:
最終的に生成された HTML や JSON などのデータが、HTTP レスポンスとして Web サーバーを介してクライアント(ブラウザ)に返送されます。
全体の流れまとめ
- リクエスト着弾:Web サーバーが HTTP リクエストを受け取る。
-
DispatcherServletへ転送:Web サーバーがリクエストをDispatcherServletに渡す。 -
ハンドラーの特定 (
HandlerMapping):DispatcherServletはHandlerMappingを使い、リクエスト URL に対応する Controller のメソッド(ハンドラーオブジェクト)と、その実行チェーン(インターセプター)を見つけ出す。 -
HandlerAdapterの選択・実行:DispatcherServletは、見つけたハンドラーオブジェクトを処理できる最適なHandlerAdapterを選び出し、そのHandlerAdapterが Controller のメソッドを実際に呼び出す。 -
結果の処理・レスポンス返却:Controller から返された結果を
DispatcherServletが受け取り、必要に応じてビューのレンダリングなどを行い、生成された HTTP レスポンスをクライアントに送り返す。
Discussion