🍃

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 を実行できます。

連携の仕組み

  1. HandlerAdapter の準備:
    DispatcherServlet は起動時、Spring の IoC コンテナ (ApplicationContext) から、必要なすべての HandlerAdapter のインスタンスを取得し、リストとして内部に保持します。これにより、DispatcherServlet は具体的な HandlerAdapter の実装を知らずに、コンテナが提供するアダプターを利用できます。代表的な HandlerAdapter には、@RequestMapping 付きメソッドを扱う RequestMappingHandlerAdapter や、古い Controller インターフェースを扱う SimpleControllerHandlerAdapter などがあります。

  2. 実行時の選択:
    リクエストが来てハンドラーオブジェクトが特定されると、DispatcherServlet は持っている HandlerAdapter のリストを順に確認します。それぞれの HandlerAdapter「このハンドラーを処理できますか?」(supports() メソッド) と問い合わせ、最初に「はい、できます!」と答えた HandlerAdapter が選ばれます

  3. Controller メソッドの実行:
    選ばれた HandlerAdapter が、特定された Controller のメソッドを実際に呼び出し、ビジネスロジックを実行させます。この際、リクエストパラメータの解決(例: @RequestParam@PathVariable など)も行われます。

  4. 結果の処理:
    Controller から返された結果(例えば ModelAndView オブジェクトや、直接返すデータなど)は DispatcherServlet に渡されます。

    • DispatcherServlet は、もし結果が論理的なビュー名(例: "home")であれば、ViewResolver を使って、そのビュー名から実際のビューオブジェクト(例: home.jsp)を見つけ出し、モデルデータと共にレンダリング(HTMLなどを生成)します。
    • Controller が直接データを返す場合(例: @ResponseBody アノテーションを使用している場合)、そのデータが HTTP レスポンスのボディに直接書き込まれます。
  5. レスポンスの返却:
    最終的に生成された HTML や JSON などのデータが、HTTP レスポンスとして Web サーバーを介してクライアント(ブラウザ)に返送されます。


全体の流れまとめ

  1. リクエスト着弾:Web サーバーが HTTP リクエストを受け取る。
  2. DispatcherServlet へ転送:Web サーバーがリクエストを DispatcherServlet に渡す。
  3. ハンドラーの特定 (HandlerMapping)DispatcherServletHandlerMapping を使い、リクエスト URL に対応する Controller のメソッド(ハンドラーオブジェクト)と、その実行チェーン(インターセプター)を見つけ出す。
  4. HandlerAdapter の選択・実行DispatcherServlet は、見つけたハンドラーオブジェクトを処理できる最適な HandlerAdapter を選び出し、その HandlerAdapter が Controller のメソッドを実際に呼び出す。
  5. 結果の処理・レスポンス返却:Controller から返された結果を DispatcherServlet が受け取り、必要に応じてビューのレンダリングなどを行い、生成された HTTP レスポンスをクライアントに送り返す。
GitHubで編集を提案

Discussion