【春のソースコード分析】春の豆の循環依存

Beanのライフサイクルリファレンス:https //blog.csdn.net/sumengnan/article/details/113702527

 

1.第3レベルのキャッシュとは何ですか?

  1. レベル1キャッシュ(シングルトンプール): singletionObjects ConcurrentHashMap <beanName、beanobject>
  2. 第2レベルのキャッシュ(初期のシングルトンオブジェクト): earlySingletionObjects HashMap <beanName、不完全なBeanオブジェクト>(主に循環依存の問題を解決するために使用されます)
  3. 3レベルキャッシュ(シングルトンファクトリ): singletonFactories HashMap <beanName、ObjectFactory(lambda expression)>(循環依存があり、準備作業を行うのと同等のaopが必要な場合にのみ使用されます)

ちなみに、なぜ第1レベルのキャッシュはConcurrentHashMapを使用するのに、第2レベルと第3レベルのキャッシュはHashMapを使用するのでしょうか。

第1レベルのキャッシュはシングルトンBeanを格納するため、スレッドセーフである必要があります。

2番目と3番目のレベルのキャッシュはペアであり、スレッドの安全性を確保するために使用されるすべての場所に同期ロックが追加されているため、HashMapを直接セキュリティに使用できます。図に示すように:

シングルトンBeanとシングルトンモードの違いは何ですか?

  1. シングルトンモード:オブジェクトは1つしか存在できないことを示します。
  2. シングルトンBean:Bean名が異なる限り、オブジェクトが同じであるかどうかは関係ありません。

 

2.Beanのライフサイクルの簡単な説明

通常のBeanのライフサイクルは次のとおりです。

  1. Beanオブジェクトをインスタンス化します。
  2. 属性を入力します。
  3. その他の操作の実行(簡単な説明)
  4. シングルトンプールに入れる(第1レベルのキャッシュ)

例えば:

例:aServiceクラスはbServiceクラスに依存し、bServiceはaServiceに依存します

@Component("aService")
public class AService {
    @Autowired
    private BService bService;

    public BService getBService() {
        System.out.println(bService);
        return bService;
    }
}

=====================
@Component("bService")
public class BService {
    @Autowired
    private AService aService;
    
    public AService getAService() {
        System.out.println(aService);
        return aService;
    }
}

最初にaServiceを初期化してもbServiceを初期化しても、結果は同じです。次に、最初にaServiceを処理します

この時点でのaServiceのライフサイクルは次のとおりです。

     1. aServiceオブジェクトをインスタンス化します(スプリングの最下層は反射によって取得されます)。

     2. bServiceのプロパティを入力します-> シングルトンプールからのクエリ(第1レベルのキャッシュ) ->見つからない場合はbServiceをインスタンス化します

               以下のbServiceのライフサイクルを開始します。

                 2.1.bServiceオブジェクトをインスタンス化します。

                 2.2.aServiceオブジェクトにデータを入力します ->シングルトンプールからクエリを実行します(第1レベルのキャッシュ) -> aServiceが見つからない場合はインスタンス化します

                 2.3。aServiceオブジェクトのインスタンス化に移動します->繰り返し続けます  (循環依存)

上記はスプリングと呼ばれ、循環依存の問題があります。

の解き方?

仲介者(キャッシュ)を追加します。これは第2レベルのキャッシュです。

この時点で、aServiceのライフサイクルは次のようになります。

     1.aServiceオブジェクトをインスタンス化します ->それを第2レベルのキャッシュMap <aService、aService不完全オブジェクト>に配置します。

     2.bService属性を入力します-> シングルトンプールからのクエリ(第1レベルのキャッシュ) ->見つからない場合は、第2レベルのキャッシュに移動してクエリを実行します->見つからない場合は、bServiceをインスタンス化します

               以下のbServiceのライフサイクルを開始します。

                 2.1.bServiceオブジェクトをインスタンス化します ->それを第2レベルのキャッシュに配置しますMap <bService、bService incompleteobject>

                 2.2.aServiceオブジェクトに入力します -> シングルトンプールからクエリを実行します(第1レベルのキャッシュ) ->クエリを実行するために第2レベルのキャッシュに移動します->見つかりました

                 2.3。その他の操作を実行する

                 2.4、bServiceオブジェクトをシングルトンプールに入れます

     3.その他の操作を実行します

     4.aServiceオブジェクトをシングルトンプールに配置します

この時点で、循環依存の問題は解決されています。

注:上記の手順2.2では、aServiceの不完全なオブジェクトをbServiceの属性に割り当てました。上記の手順には「問題」があるように見えますが、実際には問題はありません。

 

なぜ春はまだ3レベルのキャッシュが必要なのですか?

Beanに循環依存関係があり、aopを実行する必要がある場合、第2レベルのキャッシュは機能しません。

例:aServiceの通常のaop操作のライフサイクル:

  1. Beanオブジェクトをインスタンス化します。
  2. 属性を入力します。
  3. その他の操作(aopを含む)を実行する
  4. シングルトンプールに入れる(第1レベルのキャッシュ)

通常のaop操作が3番目のステップで実行されていることがわかります。ただし、bServiceにはaService属性が必要なため、循環依存関係がある場合は、事前にaopを実行して、それを2番目のステップに入れる必要があります

 

Beanに循環依存の問題があると判断するにはどうすればよいですか?

Setコレクション追加して、作成中のBeanを保存できます。2番目のステップでは、bServiceが依存するaServiceが作成されている場合、循環依存の問題があると判断されます。必要に応じて、事前にaopを実行してください。

 

最終的に、循環依存関係aServiceのライフサイクルは次のように完全に解決されます。

     0、singletonsCurrentlyInCreation セットコレクション<aService>(作成中のBeanを表す)

     1.aServiceの不完全なオブジェクトをインスタンス化します-> 3レベルのキャッシュに入れます<aService、aServiceの元のオブジェクト、beanName、beanDefinition>(Springの下部で使用されるラムダ式プログラミング。これらの3つの情報を使用して、 aopを実行します)

     2.bService属性を入力します-> シングルトンプールからのクエリ(第1レベルのキャッシュ) ->見つからない場合は、第2レベルのキャッシュに移動してクエリを実行します->見つからない場合は、bServiceをインスタンス化します

               以下のbServiceのライフサイクルを開始します。

                 2.0、singletonsCurrentlyInCreation Set集合<bService>

                 2.1.bServiceの不完全なオブジェクトをインスタンス化して取得します

                 2.2 aServiceオブジェクトに入力します。  - > シングルトンプール(最初のレベルのキャッシュ)からの照会をした場合> -見つからない、クエリに二次キャッシュに行く > -ことがわかっあり> - aServiceが作成されています循環依存->最初のパス第3レベルのキャッシュはラムダ式を取得し、それを実行して元のオブジェクトを取得します->必要に応じて、aopを実行してプロキシオブジェクトを取得します->第2レベルのキャッシュに配置します。

図に示すように:

ポストプロセッサInfrastructureAdvisorAutoProxyCreator実装クラスは、aopプロキシを実行するために使用されます。親クラスAbstractAutoProxyCreatorには、事前にaopを実行し、通常どおりaopを実行するための次の2つのメソッドがあります。

EarlyProxyReferencesのマップを使用して、aopが事前に実行されているかどうかを確認します。実行されている場合、aopは通常の手順では実行されません。

拡張の質問:

ラムダ式が1回だけ実行されてから削除され、結果が2次キャッシュに入れられるのはなぜですか?

シングルトンを確保するために。ラムダが複数のaop操作を行わないようにします。

                 2.3。その他の操作を実行する

                 2.4、bServiceオブジェクトをシングルトンプールに入れます

                 2.5、singletonsCurrentlyInCreationSet.remove(bService)

     3.その他の操作を実行します

     4.aServiceオブジェクトをシングルトンプールに配置します

     5、singletonsCurrentlyInCreationSet.remove(aService)

 

3. @Lazyアノテーションが循環依存を解決できるのはなぜですか?

原則:
1。Aの作成:A a = new A();
2.属性インジェクション:Bが必要であることがわかり、フィールドbのすべてのアノテーションをクエリし、@ lazyアノテーションがあることがわかった場合、直接ではありません。 Bを作成しますが、動的を使用しますエージェントはエージェントクラスBを作成します


3.このとき、AとBは相互に依存せず、プロキシクラスB1に依存するAになり、Aに依存するBになります。

 

4. Springは、マルチスレッド環境で不完全なBeanが読み取られるのをどのように防ぎますか?

2つのロックが追加されました。

1.Bean作成プロセス全体にロックが追加されます。

2.もう1つは、キャッシュからデータをフェッチするために追加されます

getBeanの開始時にキャッシュを最初にチェックする必要があり、そうでない場合はBeanが作成されるためです。

おすすめ

転載: blog.csdn.net/sumengnan/article/details/113778927