序文
前の記事に続いて、Spring Decryption -XML ParsingとBean登録は、ソースコードの分析を続けましょう。これ以上苦労せずに、詳細な紹介を一緒に見てみましょう。
復号化
SpringのXML構成には、宣言には2つの主要なカテゴリがあります。 1つは<bean id="person"/>などのデフォルトのもので、もう1つは<tx:annotation-driven />などのカスタムです。 2つのタグには、非常に異なる解析方法があります。 ParseBeanDefinitionsメソッドは、異なるタグで使用される解析方法を区別するために使用されます。 node.getNamespaceURI()メソッドを介して名前空間を取得し、デフォルトの名前空間かカスタムネームスペースであるかを決定し、春の固定名前空間http://www.springframework.org/schema/beansと比較します。一貫している場合は、 parseDefaultElement(ele, delegate);それ以外の場合は、 delegate.parseCustomElement(ele);
デフォルトタグの解析
ParsedeFaultElementは、4つの異なるタグのインポート、エイリアス、豆、豆に対して異なる処理を行っています。その中でも、Beanタグの分析は最も複雑で重要です。そのため、Beanから詳細な分析を開始します。このタグの分析プロセスを理解できれば、他のタグの分析は自然に解決されます。前の記事では、簡単に説明します。この記事では、分析モジュールについて詳しく説明します。
パブリッククラスdefaultBeanDefinitionDocumentReaderを実装するBeanDefinitionDocumentReader {private void parsedefaultelement(Element ele、beandefinitionparserdelegate Delegate){// Delegate.nodenameequals(ele、import_element){importbeandefinitionresurce(ele); } //エイリアスタグの解析else if(delegate.nodename quals(ele、alias_element)){processaliaSregistration(ele); } // beanタグ解像度else if(delegate.nodename quals(ele、bean_element)){processbeandefinition(ele、delegate); } //タグ解像度のインポートelse if(delegate.nodename quals(ele、nested_beans_element)){// beansタグ解像度再帰方法doregisterbeandefinitions(ele); }}}まず、クラスのprocessBeanDefinition(ele, delegate)を分析しましょう
veandefinitionholder bdholder bdholder = Defegate.parseBintionelement(ele)を解析する要素のbeanDefinitionDelegateクラスのParseBeanDefinitionDelegateクラスのParseBeanDefinitionDelegateクラスのParseBeanDefinitionDelement ClassのParseBeanDefinitionDelementメソッドを保護してください。 if(bdholder!= null){//返されたbdholderが空でない場合、デフォルトラベルの子ノードの下にカスタム属性がある場合、カスタムラベルを再度解析する必要があります。 try {//解析が完了したら、解析されたbdholderを登録する必要があります。登録操作は、BeanDefinitionReaderutils.registerBeanDefinition(bdholder、getReaderContext()。getRegistry())の登録beandefinitionメソッドに委任されます。 } catch(beanDefinitionStoreException ex){getReaderContext()。エラー( "beanの定義をname '" + bdholder.getbeanname() + "'"、ele、ex); } //最後に、関連するリスナーにBeanがgetreadercontext()。firecomponentRegistered(new BeanComponentDefinition(bdholder))がロードされていることを通知するための応答イベントが発行されます。 }}このコードで:
スプリングが各タグとノードをどのように解析するかを詳細に分析しましょう
Beanタグ分析
Public Class BeanDefinitionParserDelegate {@Nullable Public BeanDefinitionHolder ParseBeanDefinitionElement(Element Ele、@Nullable BeanDefinition CantainsBean){// Bean Tag string id = ele.getattribute(id_attribute); // beanタグ文字列nameattr = ele.getattribute(name_attribute)の名前属性を取得します。 list <string> aliases = new ArrayList <>(); if(stringutils.haslength(nameattr)){//名前属性の値を渡し、文字列番号(つまり、複数の名前が構成ファイルで構成されている場合、ここで処理する)文字列[] namearr = stringutils.tokenizetostringarray(nameattr、multi_value_attribute_delimiters); Aliase.Addall(arrays.Aslist(namearr)); } string beanname = id; // IDが空の場合、設定された名属性をIDとして使用しますif(!stringutils.hastext(beanname)&&!aliase.isempty()){beanname = aliase.remove(0); if(logger.isdebugenabled()){logger.debug( "no xml 'id' fied -'" + beanname + "'をBean名として、「 + Aliase +」を使用して、エイリアス"); }} if(containingbean == null){// beanname and aliaseの単一性を検証する//内部コアは、使用済みのコレクションを使用して、すべての使用済みのbeannameとairasesuniqueness(beanname、aliases、ele)を保存することです。 } //さらにすべてのプロパティをgenericbeandefinitionオブジェクトにさらに解析しますabstractbeandefinition beandefinition = parsebeandefinitionelement(ele、beanname、contadedbean); if (beanDefinition != null) { // If the bean does not specify beanName, then use the default rule to generate beanName for this bean if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readercontext.getRegistry()、true); } else {beanname = this.readercontext.generebebeanname(beandefinition); //可能であれば、プレーンビーンクラス名のエイリアスを登録します。 //これは、スプリング1.2/2.0の後方互換性で予想されます。 string beanclassname = beandefinition.getBeanClassName(); if(beanclassname!= null && beanname.startswith(beanclassname)&& beanname.length()> beanclassname.length()&&!this.readercontext.getregistry()。isbeannameinuse()。 }} if(logger.isdebugenabled()){logger.debug( "no xml 'id' nor 'name' neame 'quatived-" + "generated bean name [" + beanname + "]"); }} catch(Exception ex){error(ex.getMessage()、ELE); nullを返します。 }} string [] aliasesArray = stringutils.toStringArray(ALIASES); // beandefinitionholderオブジェクトに情報をカプセル化します新しいbeandefinitionholder(beandefinition、beanname、aliasesArray); } nullを返します。 }}この方法は、主にID、名前、エイリアスなどの関連属性を処理し、BeanNameを生成し、オーバーロード関数parseBeanDefinitionElement(ele, beanName, containingBean)メソッドのコアタグ解析を完了します。
次に、 parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean)
タグ解析操作がどのように完了するかを確認してください
Beanノードと属性分析
@NullablePublic AbstractBeanDefinition ParseBeanDefinitionElement(Element Ele、String BeanName、@Nullable BeanDefinition ContainsBean){this.Parsestate.push(new Beannentry(Beanname))); // beanタグ文字列classname = nullのクラス属性を取得します。 if(ele.hasattribute(class_attribute)){classname = ele.getattribute(class_attribute).trim(); } // Beanタグ文字列の親属性を取得します親= null; if(ele.hasattribute(parent_attribute)){parent = ele.getattribute(parent_attribute); } try {// abstractbeandefinitionを作成して属性をホストするabstractbeandefinition bd = createbeandefinition(classname、parent); // Bean Tag ParseBeanDefinitionAttributes(ELE、BEANNAME、containingBean、BD)のさまざまな属性を取得します。 // parse description tag bd.setdescription(domutils.getChildElementValueByTagname(ELE、description_Element)); // Parse Meta Tag Parsemetaelements(ELE、BD); // parse lookup-method tag parselookupoverridesubelements(ele、bd.getmethodoverrides()); // parse交換されたメソッドタグparsereplacedmethodsubelements(ele、bd.getmethodoverrides()); // parse交換されたメソッドタグparsereplacedmethodsubelements(ele、bd.getmethodoverrides()); // parse constructor-argタグparseconstructorargelements(ele、bd); //プロパティタグparsepropertyelements(ele、bd); // Parse Qualifier Tag PareashalifierElements(ELE、BD); bd.setResource(this.readercontext.getResource()); bd.setsource(extractsource(ele)); BDを返します。 } catch(classNotFoundException ex){error( "bean class [" + classname + "] not not in"、ele、ex); } catch(noclassdeffounderror err){error( "class that bean class [" + classname + "]は、発見されていない"、ele、err); } catch(throwable ex){error( "bean definition parsing"、ele、ex); }最後に{this.parsestate.pop(); } nullを返します;}さらに他の属性と要素(多くの要素と属性があるため、これは巨大なワークロードです)を解析し、それらをgenericbeandefinitionにパッケージ化します。これらの属性と要素を解析した後、Beanに指定されたBeanNameがないことを検出した場合、デフォルトのルールを使用してBeanのBeanNameを生成します。
// beandefinitionParserdelegate.javaprotected abstractbeandefinition createbeandefinition(@nullable string classname、@nullable string parentname)classnotfoundexception {return beandefinition readerutils.createbeandefinition(parredname、classname、edirecontextextextextextextextextextextextionクラスBeanDefinitionReaderutils {public static AbstractBeanDefinition CreateBeanDefinition(@Nullable String ParentName、@Nullable String ClassName、@Nullable ClassLoader ClassLoader)SLOWS ClassNotFoundException {GenericBeanDefinition BD = New GenericBeanDefinition(); // parentNameは空のbd.setParentName(parentName); // classloaderが空になっていない場合//通過したクラスローダーを使用して、同じ仮想マシンをクラスオブジェクトにロードします。それ以外の場合は、classloaderのみが録音されます(classname!= null){if(classloader!= null){bd.setbeanclass(classutils.forname(classname、classloader)); } else {bd.setbeanClassName(className); }} bdを返します。 }}BeanDefinitionは容器内の<bean>の内部表現であり、Beandefinitionと<bean>は1対1です。同時に、BeanDefinitionはBeanDefinitionRegistryに登録されます。これは、Spring構成情報のメモリ内データベースのようなものです。
これまでのところ、 createBeanDefinition(className, parent);終了し、属性をホストするために使用される抽象的なdefinitionも取得しました。 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); parseBeanDefinitionAttributes(ELE、BEANNAME、containingBean、BD);豆のさまざまなタグ属性を解析します。
Public Class BeanDefinitionParserDelegate {public AbstractBeanDefinition ParseBeanDefinitionAttributes(Element Ele、String BeanName、@nullable BeanDefinition Bd、AbstractBeanDefinition BD){//ある場合、bd.set(属性); BDを返します。 }} ``「Bean」タグの完全な解析が終了しました。 「Bean」タグの下にある要素の解析は似ています。興味がある場合は、ソースコードを追跡して、「予選、lookup-method」(*「bean」より複雑ではない)などの解析方法を確認できます。次の章では、カスタムタグコンテンツの詳細について説明します。
最後に、取得した情報を「beandefinitionholder」インスタンスにカプセル化します
「Java // beandefinitionParserdelegate.java@nullablepublic beandefinitionholder parsebeandefinitionElement(要素エレ、@nullable beandefinition containsbean){//解析されたビアンデフニッションを登録します
構成ファイルを解析した後、Beanのすべてのプロパティを取得しました。次のステップはBeanを登録することです。
Public Class BeanDefinitionReaderutils {public static void RegisterBeanDefinition(BeanDefinitionHolder DefinitionHolder、BeanDefinitionRegistry Registry)BeanDefinitionStoreExceptionをスローする{// beanNameを一意の識別文字列beanName = DefinitionHolder.getBeanName(); // bean registry.registerbeandefinition(beanname、definitionholder.getBeanDefinition())のコアコードを登録します。 // Bean String [] Aliass = diffentionHolder.getAliase()のすべてのエイリアスを登録します。 if(aliases!= null){for(string alias:aliass){registry.registeralias(beanname、alias); }}}}上記のコードは主に2つの機能を完了します。1つはBeanNameを使用してBeanDefinitionを登録することであり、もう1つはエイリアスの登録を完了することです。
BeanName Register BeanDefinition
パブリッククラスDefaultListableBeanFactory {@Override public void RegisterBeanDefinition(String BeanName、BeanDefinition BeanDefinition)は、BeanDefinitionStoreExceptionをスローします{assert.hastext(beanname、 "bean name not not not not vate"); assert.notnull(beandefinition、 "beandefinitionはnullであってはならない"); if(beandefinition instance of abstractbeandefinition){try {//登録前の最後の検証、ここでの検証はxmlファイル検証とは異なります//主に抽象化の検証のためです。 beandefinition).validate(); } catch(beanDefinitionValidationException ex){新しいbeandefinitionStoreException(beandefinition.getResourcedescription()、beanName、「Bean定義の検証が失敗した」、ex); }} beanDefinition OldBeanDefinition; //キャッシュでbeandefinitionを取得oldbeandefinition = this.beandefinitionmap.get(beanname); if(oldbeandefinition!= null){//キャッシュがある場合、上書きが許可されているかどうかを判断します(!isallow andefinitionStoreException(beandefinition.getResourcedescription()、beanname、 " "':既に[" + oldbeandefinition + "]バウンドがあります。"); } else if(oldbeandefinition.getrole()<beandefinition.getrole()){// egは役割_applicationであり、現在は役割_supportまたはlele_infrastructure if(this.logger.iswarnedabled()){this.logger.warn(フレームワーク生成豆の定義:[" + oldbeandefinition +"]を[" + beandefinition +"] "に置き換える); }} else if(!beandefinition.equals(oldbeandefinition)){if(this.logger.isinfoenabled()){this.logger.info( "bean for bean '" + beanname + "'を別の定義でbeandefinition:oldefinition +"] "; }} else {if(this.logger.isdebugenabled()){this.logger.debug( "bean '" + beanname + "'の等価定義を伴うbeanの定義:[" + beandefinition + "]を[" + beandefinition + ""); }} //上書きが許可されている場合は、beandefinitionMap this.beanDefinitionMap.put(BeanName、BeanDefinition)にbeanDefinitionを保存します。 } else {// Bean Creationが開始されたかどうかを判断します(hasbeancreationstarted()){//スタートアップ時間コレクション要素をもう変更できなく(安定した反復)同期(this.beandefinitionmap){// beandefinitionにbeandefinitionmapにbeandefinitionmapに保存します。 //登録済みbeanNameリスト<String> updatedDefinitions = new ArrayList <>(this.beandefinitionNames.size() + 1); updatedDefinitions.addall(this.beandefinitionNames); updatedDefinitions.add(beanname); this.beandefinitionNames = updatedDefinitions; if(this.manualsingletonnames.contains(beanname)){set <string> updatedSinglotons = new linkedhashset <>(this.manualsingletonnames); updatedsingletons.remove(beanname); this.manualsingletonnames = updatedSingletons; }}} else {//私はまだ豆の作成を開始していません。 this.beandefinitionNames.add(beanname); this.manualsingletonnames.remove(beanname); } this.frozenbeandefinitionNames = null; } if(oldbeandefinition!= null || containssingleton(beanname)){// beanname resetbeandefinition(beanname)に対応するキャッシュをリセットします。 }}}エイリアスを登録します
BeanDefinitionを登録した後、次のステップはエイリアスを登録することです。登録されたエイリアスとBeanNameの間の対応する関係は、AliASMAPに保存されます。レジスタリアスメソッドはsimplealiaSregistryに実装されていることがわかります
public class simplealiaSregistry { / **エイリアスから標準名へのマップ* /プライベート最終マップ<string、string> aliasmap = new concurrenthashmap <>(16); public void registeralias(string name、string alias){assert.hastext(name、 "'name'は空にしてはならない"); assert.hastext(airas、 "'alias'は空にしてはならない"); if(alias.equals(name)){// beanNameがエイリアスと同じ場合、エイリアスは録音されず、対応するエイリアスthis.aliasmap.remove(エイリアス)を削除します。 } else {string registeredname = this.aliasmap.get(alias); if(RegisteredName!= null){if(Registeredname.equals(name)){//エイリアスが登録されており、指摘された名前が現在の名前と同じ場合、処理は行われません。 } //エイリアスが上書きを許可しない場合、例外がスローされます(!adrowaliaSoverriding()){新しいIllegalStateException( "nable register airas '" + alias + "' for name ' + name +"':name '" + registeredname +"'); }} //チェックループは、a-> b b-> c c-> aなどの依存関係を指します。エラーが発生します。 this.aliasmap.put(alias、name); }}} checkForAliasCircle()メソッドを介してエイリアスの回覧を確認してください。 a-> bが存在する場合、a-> c-> bが再び表示されると、例外がスローされます。
保護されたvoid checkforaliAscircle(string name、string alias){if(hasalias(alias、name)){show new lilegalstateexception( "nable reghiping '" + airas + "' for name '" + name + "':circular reference - '" + name +」 }} public boolean hasalias(string name、string alias){for(map.entry <string、string> entry:this.aliasmap.entryset()){string RegisteredName = entry.getValue(); if(RegisteredName.equals(name)){string Registeredalias = entry.getKey(); return(Registeredalias.equals(エイリアス)|| hasalias(registeredalias、alias)); }} false;}この時点で、エイリアスの登録が完了し、次のタスクが完了しました。
通知を送信します
リスナーに解析および登録されるように通知します
//DEFAULTBEANDEFINITIONDOCUMENTREADER.JAVAPROTECTEDED VOID ProcessBeanDefinition(Element ELE、BEANDEFINITIONPARSERDELEGATE DELEGATE){//登録イベントを送信します。 getReaderContext()。firecomponentRegistered(new BeanComponentDefinition(bdholder));}FirecomponentRegisteredメソッドは、リスナーに作業を解析して登録するために通知するために使用されます。ここでの実装は、拡張のためだけです。プログラム開発者が登録されたBeanDefinitionイベントを聴く必要がある場合、彼はリスナーを登録し、処理ロジックをリスナーに書き込むことができます。現在、春はこのイベントでこのイベントを処理しません
ReaderContextは、XMLBEANDEFINITIONDERのCREATEREADERCONTEXTを呼び出し、 fireComponentRegistered()を呼び出すことにより生成されます。
エイリアスタグ分析
Springは<alias name="person" alias="p"/>のエイリアス構成を提供します。タグ解像度は、ProcessAliaSregistration(Element ELE)メソッドで行われます。
public class defaultbeandefinitionDocumentReader {Protected void processaliaSregistration(element ele){// get alisaタグ名プロパティString name = ele.getattribute(name_attribute); // ALISAタグALISAタグエイリアス属性文字列エイリアスを取得= ELE.GETATTRIBUTE(ALIAS_ATTRIBUTE); boolean valid = true; if(!stringutils.hastext(name)){getReadErcontext()。エラー( "nameは空ではない"、ele); valid = false; } if(!stringutils.hastext(alias)){getReadErcontext()。エラー( "エイリアスは空ではない"、ele); valid = false; } if(valid){try {//エイリアスを登録getReaderContext()。getRegistry()。レジスタリアス(名前、エイリアス); } catch(exception ex){getReaderContext()。エラー( "Alias '" + alias + "' for bean with name '" + name + "'"、ele、ex); } //エイリアスを登録した後、リスナーに対応する処理getreadercontext()を実行するように伝えます。firealiaSregistered(name、alias、extractsource(ele)); }}}まず、エイリアスタグ属性が抽出および検証されます。検証が渡された後、エイリアス登録が実行されます。ビーンタグ分析でのエイリアス登録とエイリアス登録が行われました。ここでは繰り返しません。
タグ分析をインポートします
public class defaultbeandefinitionDocumentReader {protected void importbeandefinitionResource(element ele){//インポートタグ文字列のリソース属性を取得= ele.getattribute(resource_attribute); //存在しない場合、処理は行われません(!stringutils.hastext(location)){getReaderContext()。エラー(「リソースの場所は空ではない」、ELE);戻る; } //「$ {user.dir}」などのプレースホルダー属性形式の解析set <sesource> everityResources = new LinkedHashset <>(4); //リソースが絶対パスであるか、相対パスブールアブソリュートロケーション= falseであるかを判断します。 try {absolutelocation = resourcePatternutils.isurl(場所)||を試してくださいresourceUtils.touri(場所).isabsolute(); } catch(urisyntaxexception ex){// uriに変換できません。 altacerresources); if(logger.isdebugenabled()){logger.debug( "imported" + importcount + "url location [" + location + "]"); }} catch(beandefinitionStoreException ex){getReaderContext()。エラー( "url location [" + location + "]"、ele、ex)からBean定義をインポートできなかった。 }} else {try {int importcount; //リソースを相対パスリソースRELATIVERSOURCE = getReadErcontext()。getResource()。createrElative(location); if(relativeresource.exists()){importcount = getReaderContext()。getReader()。loadbeandefinitions(lerativeresource); everityResources.add(relativeresource); } else {string vaselocation = getReaderContext()。getResource()。getUrl()。toString(); importcount = getReaderContext()。getReader()。loadbeandefinitions(stringutils.applyrelativepath(baselecolation、location)、eacherresources); } if(logger.isdebugenabled()){logger.debug( "imported" + importcount + "相対位置からのビーン定義[" + location + "]"); }} catch(ioException ex){getReaderContext()。エラー( "現在のリソースの場所を解決できなかった"、ele、ex); } catch(beandefinitionStoreException ex){getReaderContext()。エラー( "相対位置[" + location + "]"、ele、ex)からビーン定義をインポートできなかった。 }} //解析後、リスナーのアクティベーション処理が実行されます。 resource [] actresArray = everyresources.toarray(new resource [everyresources.size()]); getReadErcontext()。fireimportprocessed(location、actresArray、extractsource(ele)); }}インポートタグの処理を完了した後、最初のことは、 <import resource="beans.xml"/>リソース属性で表されるパスを取得し、 ${user.dir}などのパス内の属性プレースホルダーを解析し、場所が絶対パスか相対パスかを決定することです。それが絶対的な経路である場合、Beanの解析プロセスは再帰的に呼ばれ(loadBeanDefinitions(location, actualResources);)別の分析を実行します。それが相対パスの場合、絶対パスを計算して解析します。最後に、リスナーに通知すると、解析が完了します。
要約します
数回未知の秋、冬、春、夏の後、すべてがあなたが望む方向に従います...
さて、上記はこの記事のコンテンツ全体です。この記事の内容には、すべての人の研究や仕事に特定の参照値があることを願っています。ご質問がある場合は、メッセージを残してコミュニケーションをとることができます。 wulin.comへのご支援ありがとうございます。
何か言って
全文コード:https://gitee.com/battcn/battcn-spring-source/tree/master/chapter1(ローカルダウンロード)