「時代遅れ」のSpringMVCはいったい何を使っているのだろうか.DispatchServiceletソースコードの詳細な分析


SpringのIOC(『ゼロベースでSpringソースを見る-IOCコントロール反転』)とAOP(『ソースコードから、Spring AOPのカットプログラミングを読む』)のソースコードを分析してきましたが、今回はSpringMVCを分析します.本稿では、現在のSpringMVCの使用状況を簡単に説明し、Demoの簡単さで初歩的な使用印象を与え、その中で実行されている配布ソースコードを印象的に見てみましょう.
果たしてSpring MVCとは何か、私たちはまだ使っていますか?
Spring MVC、公式名は実はSpring Web MVCで、Mavenのパッケージ名もspring-webmvcです.Springが誕生して以来、サーブレットApiベースのwebアーキテクチャです.Spring 5では、新しいWebアーキテクチャ、Fluxが、イベント駆動モデル(nodejsのような)に基づいて作られています.今後はFluxについて紹介する記事を書きますので、ご注目ください.
MVCは、「前世紀」で最も流行した前後端インタラクティブモデルと言える.Model(ビジネスモデル)、View(ユーザービュー)、Controller(コントローラ)を含み、各部分を別々に組織し、コードの抽象と隔離の処理はコード設計の模範と言える.
しかし15年から様々なフロントエンドフレームワークの台頭に伴い、フロントエンドバックエンドの関係はさらに進化し、MVCアーキテクチャから前後端分離のRESTアーキテクチャに変化した.以前はMVCアーキテクチャはリクエストごとにコントローラ->モデル->ビューの流れを経てフロントエンドリクエストバックエンドインタフェースに進化し,JSONに戻るRESTアーキテクチャであった.問題が来て、私たちはいったいSpringMVCを使っていますか?答えは、全部ではありません.前後にコードや配置の分離を行い,つまりバックエンドがフロントエンドの存在を感知しないため,バックエンドにとってView(ユーザビュー)は語れない.Model(ビジネスモデル)は性質上の変更を送信し,以前は1つのフロントエンドに必要なModelであり,ページに読み取り,現在はJSON形式でフロントエンドに与えられ,フロントエンドで自由に処理される.
Webフレームワークの核心として、Controller(コントローラ)は依然として残っている.だから今ではSpringMVCを使うのはControllerという層が多いです.もちろんSpringMVCには、filter、Http Caching、Web Securityなど、他のコンポーネントもあります.本稿ではMVCアーキテクチャにおけるControllerの機能に重点を置いているが,ControllerのコアコンポーネントはDispatcherServeretである.その後、Demoを通じて、DispatcherSevletがどのように要求に対して配布を制御するかを徐々に理解します.
従来のSpringMVC起動の概要
従来のSpringMVCでは、Web.xmlとアプリケーションContext.xmlを構成する必要があります.前者は、servlet、welcomeページなど、プロジェクトの初期化を担当する構成であり、JavaEEの仕様である.後者はSpring Contextを初期化する構成であり,主にBeanの構成である.
前述したように、SpringMVCはサーブレットベースのアーキテクチャであり、DispatcherServiceletはSpringMVCがすべてのリクエストを処理するサーブレットをブロックするため、web.xmlはDispatcherServiceletを構成する必要がある.他にもcontextLoaderListenerがあり、DispatcherServicelet以外のすべてのcontextコンテンツをロードするほか、contextConfigLoaderでSpringのプロファイル(applicationContext.xmlなど)を指定する必要があります.
では、プロジェクトが開始されると、web.xmlをロードしてまずcontextLoaderListenerを実行し、SpringのApplication contextを初期化します.後からHTTPリクエストが入ってくるとDispatcherServiceletに落ちて処理配布されます.
SpringBoot Web Demo構築
Spring構成注釈とSpringBootが誕生して以来、web.xmlとアプリケーションContext.xmlプロファイルを書く人はますます少なくなってきました.しかし、Dispatcherの原理を直接理解するために、DemoはSpringBootのstarterワンタッチで直接構築されています.
Webのstarter依存を直接追加
 
    org.springframework.boot
    spring-boot-starter-web
    2.0.4.RELEASE

このstarterがどのような内容を含んでいるかを見てみましょう.緑のボックスはspringMVCの依存で、赤のボックスはSpring自動構成の依存で、青のボックスはtomcatの依存です.中のSpringのバージョンは5.0.8 RELEASEです.
SpringBoot起動クラス
テストコントロール
プロジェクトを開始したら、ブラウザにhttp://localhost:8080/hello?name=Zackと入力します.結果はHello Zackを返します.
以上、SpringMVCを利用する基本的な内容ですが、SpringMVCがDispatcherServiceletを利用してブロック配布を行う方法を見てみましょう.
DispatcherServiceletソース分析
1つのリクエストが入ってくると、まず様々なfilterが実行され、最終的に必要なリクエストがフィルタリングされ、DispatcherService()メソッドに落ちます.この方法は,あらかじめいくつかの特殊な要求パラメータを設定してからdoDispatch()に転送し,本格的な処理転送を行う.
doDispatch()のコメントを見て、この方法の役割は実際に配布されたhandlerを実行することであることを説明します.
  • Handlerは、HandlerMappingの優先度によって取得される.HandlerAdapterは、DispatcherServiceletにマウントされているHandlerAdapterを問合せ、そのHandlerをサポートすることによって取得される.
  • すべてのHTTPリクエストはdoDispatch()で処理されます.具体的にどの方法でビジネスロジックを処理するかは、HandlerAdaptersまたはhandlersに依存します.

  • 注釈から分かるように,配布論理コア全体はHandlerAdapterとHandlerにある.この二つはいったい何ですか.
    公式サイトの説明HandlerAdapterはDispatcherServiceletに協力して対応するhandlerを呼び出し、具体的なhandlerがどのように呼び出されたのかを無視します.たとえば、注釈形式のcontrollerを呼び出すには注釈を処理する必要があり、xml構成形式の解析プロファイルが必要です.このアダプタは、DispatcherServiceletが処理の詳細を遮断するのを助けるためです.
    Handlerについては明確に説明されていないが、debugソースコードは、Handlerが実際に具体的な処理を必要とする方法(下図の赤枠と上のDemoのcontrollerを比較する)に割り当てられていることを発見することができる.
    doDispatch()というメソッドのソースコードに戻ると、getHandler()、getHandler Adapter()がHandlerとHandler Adapterを取得する場所です.
    getHandler()
    getHandler()ソースコード全体を見ると数行ですが、2つの点に注意してください.1つは、HandlerではなくHandlerExecutionChainタイプを返すことです.HandlerExecutionChainは実際にはHandlerのパッケージであり、Handlerのいくつかの前置および後置の操作を実行するためにHandlerに対応するinterceptorブロッキングを含む.
    もう一つ、HandlerExecutionChainはhandlerMappingsを順番に巡って出しています.HandlerMappingは何ですか?公式サイトの説明から分かるように、それはリクエストとhandler(実際にはHandlerExecutionChain)の関連Mapであり、通俗的にはルーティングと処理ロジックの関連である.主に2つの実装があり、1つはRequestMappingHandlerMapping(注釈形式メソッドをサポート)であり、もう1つはSimpleUrlHandlerMapping(登録されたURIリソースを表示するメンテナンス)である.
    これによりSpringが起動すると,注釈,登録された静的リソースがスキャンされ,このhandlerMappingsが初期化されることが推測される.具体的なロジックはDispatcherServiceletのinitHandlerMappingsメソッド内にあります.初期化の方法には、主に3つのステップがあります.
  • SpringのApplicationContextからHandlerMappingのBean
  • を取り出す.
  • 上から取り出したBeanを優先順位付けし、主に@Order注記の順位付け
  • である.
  • Beanが取り出せない場合は、デフォルトのポリシーを使用します.

  • 3つ目のデフォルトポリシーでは、DispatcherServicelet.propertiesというファイルを見つけることができます.デフォルトのHandlerMapping、HandlerAdapterなどの関連クラスが構成されています.
    handlerMappingsを初期化した後、リクエストが入ってきたら、後のrequestはリクエストのルーティングでHandlerMappingと比較し、最後にHandler(HandlerExecutionChain)を見つけます.
    getHandlerAdapter()
    実際に処理されたHandlerを取り出した後、supportのアダプタ(Handler Adapter)を見つける必要があります.前述のHandlerAdapterの説明によれば、DeportというHandlerは必ずRequestMappingHandlerAdapterである.
    このロジックも非常に簡単で、同じように初期化されたhandlerAdapters(初期化のプロセスはhandlerMappingsに似ている)を巡り、特定のhandlerAdapterごとにsupport()メソッドを呼び出して、サポートされているかどうかを確認します.
    supports()メソッドも簡単で、handlerが自分でサポートしているクラスをinstanceofで判断します.
    HandlerAdapter.handle()
    HandlerメソッドとHandler Adapterを取得した後、実際にはHandlerメソッドのみを呼び出すHandler Adapterのhandleメソッドを実行できます.
    Demoの例に従って,HttpRequestHandlerAdapterのhandle()メソッド実装を見る.この方法の中にはHttpServeretのRequestとReponseで私たちが書いたcontrollerの中を呼び出す方法があります.なお、このメソッドはModelAndViewを返しますが、Restアーキテクチャに基づいては現在使用されていないのでnullを返します.
    Handlerの前置後置処理
    前述したHandlerはHandlerExecutionChainにカプセル化されており、前置後置のブロックも含まれています.したがって,HandlerAdapter.handle()を実行する前後にHandlerExecutionChainへの呼び出しがあり,interceptorによる前後置処理を実行する方法がある.
    具体的にはinterceptorを実行するpreHandle()とpostHandle()の方法である.
    振り返ってみると、ここの前後処理には何が含まれているのでしょうか.HandlerInterceptor注記には、U s e r o l e AuthorizationInterceptor(ユーザー権限のチェック)、LocaleChangeInterceptor(ローカル時間の変更)、ThemeChangeInterceptor(現在のトピックの変更)の3つの実装クラスが説明されています.HandlerInterceptorは基本的に要求のいくつかの前処理と結果パッケージであることがわかる.
    まとめ
    以上がSpringMVCにおけるDispatcherServiceletの基本手順である.以下に、上記の内容をまとめます.
  • 前後のアーキテクチャの進化はSpringMVCの使用を変化させ、「C」に重点を置いた.
  • "C"のコアはDispatcherServeretのdoDispatcher()メソッドにあります.
  • requestのルーティングを利用して、初期化されたhandlerMappingsとhandlerAdaptersからhandlerとhandlerAdapterを比較する.
  • handlerはHandlerExecutionChainにカプセル化されており、handlerの前後ブロックも含まれている.
  • は最後にアダプタモードを利用してHandlerAdapter.handler()メソッドを呼び出してhandlerの具体的な処理のビジネスロジックを実行する.
  • は、特定のビジネスロジックを実行する前に、HandlerExecutionChainにカプセル化されたブロッキングを実行する.

  • 詳細な技術記事、素晴らしい干物、ブログに注目してください:zackku.com微信公衆番号:Zack説コード