Spring MVC: リクエストがControllerに届くまで
経緯
Spring Framework を使っていてふと「リクエストを受けてから、どうやって Controller のメソッドが実行されるんだろう?」という疑問が湧いたので、その仕組みを調べた記録です。
DispatcherServlet
の役割
1. リクエストの入り口: Spring MVC アプリケーションに HTTP リクエストが届くと、まず DispatcherServlet
というサーブレットが受け取ります。DispatcherServlet
は Spring MVC の「司令塔」とも言える存在で、すべてのリクエスト処理の中心となります。
その役割は大きく分けて以下の通りです。
- リクエストの受付: Web サーバー(Tomcat など)が受け取った HTTP リクエストを最初に処理します。
- 適切な担当者の特定: リクエストの内容(URL や HTTP メソッドなど)に応じて、どの Controller のどのメソッドが処理すべきかを特定します。
- Controller の実行: 特定した Controller を実際に呼び出し、ビジネスロジックを実行させます。
- 結果の処理: Controller の実行結果(表示する画面や返すデータなど)を受け取り、クライアントに返すレスポンスを生成します。
HandlerMapping
2. リクエストのルーティング: DispatcherServlet
は、リクエストを受け取ると、まず HandlerMapping
を使って、そのリクエストを処理すべき Controller (正確には、ハンドラーオブジェクト) を見つけ出します。
-
何をするの?
リクエストの URL や HTTP メソッド(GET, POST など)を見て、「このリクエストは、あの Controller のこのメソッドに任せよう」とマッピング(紐付け)します。 -
どうやって見つけるの?
Spring Boot のような現代の Spring アプリケーションでは、主に@Controller
や@RestController
クラスに付与された@RequestMapping
、@GetMapping
、@PostMapping
などのアノテーションを解析してマッピングを行います。この役割を担うのが、RequestMappingHandlerMapping
というHandlerMapping
の実装クラスです。
HandlerMapping
は、リクエストを処理するハンドラーを見つけるだけでなく、そのハンドラーの実行前後に適用されるべき インターセプター(HandlerInterceptor
)のリストも一緒に DispatcherServlet
に返します。これを HandlerExecutionChain
と呼びます。
HandlerAdapter
とその連携
3. Controller の実行と結果の処理: 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