0要約
この記事では、SpringMVCのプロセッサマッピングリンク、つまりコントローラーを見つける詳細なプロセスについて簡単に説明します。
1 SpringMVC要求プロセス
コントローラーは、上記の画像に対応する手順1〜2を検索します
SpringMVC詳細な操作フローチャート
2 SpringMVC初期化プロセス
2.1最初に2つのカテゴリを理解します
1.RequestMappingInfo
カプセル化リクエストマッピングアノテーション
HTTP要求ヘッダーに関する関連情報が含まれています
インスタンスは、リクエストマッピングアノテーションに対応します
2.Handlermethod
コントローラーをカプセル化するためのリクエストを処理する方法
メソッドが属するBeanオブジェクト、メソッドに対応するメソッドオブジェクト、メソッドのパラメーターなどが含まれます。
RequestMappingHandLermapping継承関係
SpringMVC初期化中
最初にRequestMappingHandLermappingのAfterPropertiessetを実行します
次に、abstracthandlermethodMapping abdypropertiessetを入力します
この方法は、このクラスのinithandlermethodsを入力します
ApplicationContextから豆をスキャンし、豆からプロセッサメソッドを見つけて登録する責任
// ApplicationContextで豆をスキャンし、ハンドラー方法を検出して登録します。 getApplicationContext()。getBeanNamesFortype(object.class)); //(string beanname:beannames)のbeannameアレイを転送{// ishandlerは、Beanの定義に、Beanに基づいてコントローラー注釈または要求マッピング注釈が含まれているかどうかを判断します。 if(ishandler(getApplicationContext()。getType(beanName))){detecthandlermethods(beanname); }} handlermethodsInitialized(gethandlermethods());} RequestMappingHandLermapping#iShandler
上記の方法は、現在のBean定義にコントローラーの注釈または要求マッピング注釈が含まれているかどうかを判断することです。
リクエストマッピングのみが有効になった場合は?いいえ!
この場合、スプリングを初期化するときにクラスはスプリングビーンとして登録されず、beanNamesを横断するときにクラスが横断されないため、ここでコントローラーをコンポーントに置き換えることは問題ありませんが、これは一般的には行われていません。
Beanがハンドラーであることを確認した後、特定のハンドラー方法はBean(つまり、コントローラークラスで定義された要求処理方法)から見つかります。検索コードは次のとおりです
/** *ハンドラーのハンドラーメソッドを探します * @paramハンドラーハンドラーまたはハンドラーインスタンスの豆名 */保護されたvoid detecthandlermethods(最終オブジェクトハンドラー){//現在のコントローラーbeanclass <?> handlertype =(ハンドラーインスタンスの文字列)のクラスオブジェクトを取得しますか? getApplicationContext()。getType(((string)Handler):handler.getClass(); // getMappingFormETHODへの繰り返しの呼び出しを避けて、RequestMappingInfoインスタンスの最終マップ<メソッド、t>マッピング= new IdentityHashmap <Method、t>(); //上記と同じ、それはコントローラーのクラスオブジェクトでもあります<? //現在のBeanのすべてのハンドラーメソッドを取得する//メソッドに従って要求マッピングがあるかどうかを定義します!= null){maeth、mapping); //現在のBeanのすべてのハンドラーメソッド(メソッドメソッド:メソッド){//ハンドラーメソッドを登録し、次のメソッドを入力し、登録handlermethod(ハンドラー、メソッド、mappings.get(method)); }上記のコードには、getMappingFormethodを呼び出す2つの場所があります
メソッドとタイプレベルのリクエストマッピングアノテーションを使用してrequestmappinginfoを作成します
@Override Protected RequestMappingInfo getMappingFormethod(Method Method、class <?> handlertype){requestmappinginfo info = null; //メソッドの@requestMapping requestMapping methodannotation = annotationutils.findannotation(method、requestmapping.class); if(MethodAnnotation!= null){requestCondition <?> methodCondition = getCustomTommethodCondition(method); info = createrequestmappinginfo(methodannotation、methodcondition); //メソッドが属するBeanの@ReQUTESTMAPPING ANNOTATIONを取得しますtypeanNotation = annotations.findannotation(handlertype、requestmapping.class); if(typeannotation!= null){requestCondition <?> typecondition = getCustomTypeCondition(handLertype); // 2つの@RequestMappingアノテーション情報= createre quateestmappinginfo(typeannotation、typececodition).combine(info); }} return info; }この方法の目的は、Handlerメソッドメソッドに基づいてRequestMappingInfoオブジェクトを作成することです。まず、Mehtodにrequestmppingアノテーションが含まれているかどうかを判断します。その場合、注釈のコンテンツに基づいて直接requestmappinginfoオブジェクトを作成します。作成後、現在のメソッドにリクエストマッピングの注釈も含まれている豆にかどうかを判断します。この注釈が含まれている場合、クラスの注釈に基づいてRequestMappingInfoオブジェクトが作成されます。次に、MergeメソッドのRequestMappingInfoオブジェクトが返され、マージされたオブジェクトが最終的に返されます。ここで、detectecthandlermethodsメソッドを振り返ると、GetMappingFormethodメソッドへの2つの呼び出しがあります。私は個人的にこれを最適化できると思います。メソッドがそもそもハンドラーであるかどうかを判断するとき、作成されたrequestMappingInfoオブジェクトを後で直接保存して使用できます。つまり、RequestMappingInfoオブジェクトを作成するプロセスが欠落しています。次に、次のように、すぐにRegisterHandlermehtodメソッドを入力します
保護されたVoid RegisterHandLermethod(オブジェクトハンドラー、メソッドメソッド、Tマッピング){// HandLermethod HandLermethod NewHandLermethod = CreateHandLermethod(Handler、Method); HandLermethod Oldhandlermethod = handlermethods.get(マッピング); //構成にあいまいさがあるかどうかを確認します(oldhandlermethod!= null &&!oldhandlermethod.equals(newhandlermethod)){新しいIllegalstateException(「曖昧なマッピングが見つかった。 + allhandlermethod.getbean() + "'bean method/n" + allhandlermethod + "mapped。"); } this.handlermethods.put(マッピング、newhandlermethod); if(logger.isinfoenabled()){logger.info( "mapped /" " + mapping +" /"to" + newhandlermethod); } // @RequestMapping Annotationの値を取得し、value-> requestMappingInfoマッピングレコードをurlmap set <string> patterns = getMappingPathPattern(マッピング)に追加します。 for(string pattern:patterns){if(!getPathMatcher()。ispattern(pattern)){this.urlmap.add(pattern、mapping); }}}ここで、tのタイプはrequestmappinginfoです。このオブジェクトは、カプセル化された特定のコントローラーの下でのメソッドのリクエストマッピングアノテーションの関連情報です。リクエストマッピングアノテーションは、RequestMappingInfoオブジェクトに対応します。 HandLermethodはRequestMappingInfoに似ており、ControlELRの下で特定の処理方法のカプセル化です。まず、メソッドの最初の行を見て、ハンドラーとMehthodに基づいてHandlermethodオブジェクトを作成します。 2行目は、HandLermethodsマップを使用して、現在のマッピングに対応するHandlermethodを取得します。次に、同じ要求マッピング構成が存在するかどうかを判断します。次の構成により、ここに投げられますInvocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map...
異常な
@controller @requestMapping( "/AmavigUousTest")public class aviguoustestController {@RequestMapping(value = "/test1")@responsebody public string test1(){return "method test1"; } @requestMapping(value = "/test1")@responsebody public string test2(){return "method test2"; }} SpringMVC起動(初期化)フェーズ中にリクエストマッピング構成が曖昧さであるかどうかを確認します。これは、あいまいさをチェックするものの1つです(実行時に曖昧さをチェックする場所が後で言及されます)。次に、構成が正常であることを確認した後、RequestMappingInfoおよびHandLermethodオブジェクトがHandLermethods(LinkedHashmap)に追加され、RequestMapping Annotated値とReuqestMappingInfoオブジェクトがURLMAPに追加されます。
RegisterHandlermethodメソッドの簡単な要約
この方法には3つの主な責任があります
1.リクエストマッピングアノテーション構成が曖昧であるかどうかを確認してください。
2。handlermethodにrequestmappingmappinginfoのマップを作成します。このマップは、抽象化された補強装置のメンバー変数ハンドラーメソッドです。 LinkedHashmap。
3. abstracthandlermethodMappingおよびMultivalueMapのメンバー変数URLMAPを作成します。このデータ構造は、MAP>として理解できます。文字列タイプのキーは、処理方法でリクエストマッピングアノテーションの値を保存します。それは特定のURIです
最初に次のコントローラーがあります
@controller @requestmapping( "/urlmap")public class urlmapcontroller {@requestMapping(value = "/test1"、method = requestmethod.get)@responsebody public string test1(){return "method test1"; } @requestMapping(value = "/test1")@responsebody public string test2(){return "method test2"; } @requestMapping(value = "/test3")@responsebody public string test3(){return "method test3"; }}初期化が完了した後、抽象化されたlermethodMappingに対応するURLMAPの構造は次のとおりです。
上記は、SpringMVC初期化の主なプロセスです
検索プロセス
質問がある検索プロセスを理解するために、次のコントローラーが利用可能です
@controller @requestmapping( "/lookuptest")public class lookuptestcontroller {@requestMapping(value = "/test1"、method = requestmethod.get)@responsebody public string test1(){return "method test1"; } @RequestMapping(value = "/test1"、headers = "referer = https://www.baidu.com")@responsebody public string test2(){return "method test2"; } @requestMapping(value = "/test1"、params = "id = 1")@responsebody public string test3(){return "method test3"; } @requestMapping(value = "/*")@responsebody public string test4(){return "method test4"; }}次のようにリクエストがあります
このリクエストはどの方法を入力しますか?
リクエストを受け取った後、Webコンテナ(Tomcat、Jetty)が処理のためにDispatcherServletに引き渡されます。 Frameworkservletは、対応するリクエストメソッド(例:get calls doget)を呼び出し、ProcessRequestメソッドを呼び出します。 ProcessRequestメソッドを入力した後、一連の処理後、並んでDoserviceメソッドを入力します:936。次に、line856にdodispatchメソッドを入力します。現在要求されているプロセッサハンドラーをラインに入手してください:896。次に、abstracthandlermethodMappingのlookuphandlermethodメソッドを入力します。コードは次のとおりです
保護されたHandlermethod lookuphandlermethod(string lookuppath、httpservletrequest request)スロー例外{list <match> matches = new arraylist <match>(); // URIリストに基づいてDirectPathMatchesを取得<t> directPathMatches = this.urlmap.get(lookuppath); if(directPathMatches!= null){addMatchingMappings(directPathMatches、matches、request); } //直接一致するrequestmappinginfoはありません。すべてのrequestmappinginfo if(matches.isempty()){//すべてのマッピングを通過する以外に選択肢はありません(this.handlermethods.keyset()、一致、リクエスト); } //最高の一致requestmappinginfoに対応するhandlermethodを取得しますif(!matches.isempty()){comparator <match> comparator = new MatterComparator(getMappingComparator(request)); collections.sort(matches、comparator); if(logger.istraceEnabled()){logger.trace( "fund" + matches.size() + "[" + lookuppath + "]:" +一致のマッピングマッピング(s); } //構成のあいまいさをもう一度チェックしますbestmatch = matches.get(0); if(matches.size()> 1){match secondbestmatch = matches.get(1); if(comparator.compare(bestmatch、secondbestmatch)== 0){メソッドM1 = bestMatch.Handlermethod.getMethod();方法M2 = SecondBestMatch.Handlermethod.getMethod();新しいIllegalStateException( "HTTP PATH用にマッピングされた曖昧なハンドラーメソッド '" + request.getRequesturl() + "':{" + m1 + "、" + m2 + "}"); }} handlematch(bestmatch.mapping、lookuppath、request); bestmatch.handlermethodを返します。 } else {return handlenomatch(handlermethods.keyset()、lookuppath、request); }} LookUppath = "/lookuptest/test1" Lookuppath、つまり要求されたURIでlookuppath = "/lookuptest/test1"でlookuphandlermethodメソッドを入力します。 urlmapを直接見つけて、直接一致するrequestmappinginfoリストを取得します。ここでは、3つのRequestMappingInfosを一致させます。次のように
次に、AddMatchingMappingsメソッドを入力します
private void addMatchingMappings(コレクション<t>マッピング、リスト<match>マッチ、httpservletrequest request){for(t mapping:mappings){tmate = getMatchingMapping(マッピング、リクエスト); if(match!= null){matches.add(new match(match、handlermethods.get(マッピング))); }}}この方法の責任は、現在の要求されているURIとマッピングのRequestMappingInfoが一致するかどうかを横断することです。もしそうなら、同じRequestMappingInfoオブジェクトを作成します。次に、RequestMappingInfoに対応するHandLermethodを取得します。次に、マッチオブジェクトを作成し、マッチリストに追加します。 AddMatchingMappingsメソッドを実行した後、LookUpHandLermethodに戻ります。現時点では、一致する可能性のある3つのRequestMappingInfoオブジェクトがまだあります。次のプロセスは、マッチャーリストを並べ替えて、リストの最初の要素を最高の一致として取得することです。一致のハンドライトドを返します。ここでは、RequestMappingInfoの比較方法を入力し、特定のソートロジックをご覧ください。コードは次のとおりです
public int compareto(requestmappinginfo otth、httpservletrequest request){int result = patternscondition.compareto(other.getpatternscontition()、request); if(result!= 0){return result; } result = paramscondition.compareto(other.getParamScondition()、request); if(result!= 0){return result; } result = headerscondition.compareto(other.getheaderscondition()、request); if(result!= 0){return result; } result = consumerCondition.comPareto(other.getConsumesCondition()、request); if(result!= 0){return result; } result = produceScondition.comPareto(other.getProduceScondition()、request); if(result!= 0){return result; } result = methodscondition.compareto(other.getMethodScondition()、request); if(result!= 0){return result; } result = customconditionholder.compareto(other.customconditionholder、request); if(result!= 0){return result; } return 0;}コードでわかるように、一致の順序はvalue> params> headers> consumes>生成>メソッド>カスタムです。これを見て、前の質問に簡単に答えることができます。同じ値の場合、PARAMSは最初に一致できます。そのため、そのリクエストはtest3()メソッドを入力します。 lookuphandlermethodに戻り、Handlermethodを見つけます。 SpringMVCは、ここで構成のあいまいさをもう一度確認します。ここでのチェックの原則は、2つのRequestMappingInfosを最高の程度と比較することです。 SpringMVCを初期化するとき、あいまいな構成チェックがあるという質問があるかもしれませんが、なぜここでもう一度チェックされるのですか?現在、コントローラーに2つの方法がある場合、次の構成を初期化のあいまいさで確認できます。
@RequestMapping(value = "/test5"、method = {requestmethod.get、requestmethod.post})@responsebublic string test5(){return "method test5";}@requestmapping(value = "/test5"、method = {requestmethod.get、requestmethod.delete}) http:// localhost:8080/springmvc-demo/lookuptest/test5リクエストを実行すると、lookuphandlermethodメソッドでスローされますjava.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path 'http://localhost:8080/SpringMVC-Demo/LookupTest/test5'例外。 RequestMethodsRequestConditionの比較方法が比較方法番号であるため、例外はここでスローされます。コードは次のとおりです
public int compareto(requestmethodsrequestcondition otter、httpservletrequest request){return other.methods.size() - this.methods.size();}ワイルドカードはいつ一致しますか?値に直接一致するRequestMappingInfoがURLMAPを使用して取得できない場合、WildCardマッチングを使用してAddMatchingMappingsメソッドを入力します。
要約します
上記は、この記事のコンテンツ全体です。この記事の内容には、すべての人の研究や仕事に特定の参照値があることを願っています。ご質問がある場合は、メッセージを残してコミュニケーションをとることができます。 wulin.comへのご支援ありがとうございます。