何?SpringBootプロジェクトは、循環依存関係の例外の報告を開始します
今日、私はプロジェクトを開始し、周期的な依存関係の例外を報告しました。説明のために、ここで簡略化しました。
私のコードはこんな感じです(シミュレート)
@Component
public class TestA {
@Autowired
private TestB testB;
@Async("taskExecutor")
public TestB getTestB(){
return testB;
}
}
@Component
public class TestB {
@Autowired
private TestA testA;
public void testTrans() {
testA.getTestA();
System.out.println("testTrans线程名称:" + Thread.currentThread().getName());
}
}
ここで、TestAはTestBを呼び出し、TestBはTestAを呼び出します。これは典型的な循環依存のシナリオですが、Springが循環依存の問題を処理することはわかっています。しかし、なぜここにエラーがあるのですか?
循環依存関係
この目的のために、Springが循環依存性の問題をどのように解決するかを分析しましょう。
循環依存
どの状況が循環依存であるかを見てみましょう。
オブジェクトMの作成はSの作成に依存し、Sの作成はMの作成に依存します。
循環依存関係の定義1
オブジェクトMの作成はSの作成に依存し、Sの作成はOの作成に依存し、Oの作成はMの作成に依存します。これも閉ループを形成します。
循環依存関係の定義2
自立もあります。
循環依存関係の定義3
Beanインスタンス化プロセス
SpringコンテナがBeanプロセスを取得する方法を見てみましょう。
最初にgetBean()
メソッドを入力しdoGetBean()
ます。メソッドは内部で呼び出されます。
doGetBean()
メソッドではgetSingleton()
、sharedInstance
インスタンスを取得するためにメソッドが呼び出されます。
getSingleton()
以下の方法:
この方法は、主にキャッシュからBeanのシングルトンを取得するためのものであり、現在取得する単一の列に循環依存がある場合は、他のキャッシュから取得するものと判断します。まず、Beanインスタンスが主に取得されるキャッシュを見てみましょう。
「「」
singletonObjects
earlySingletonObjects
singletonFactories
キャッシュされたデータ構造はマップです。
では、これらのキャッシュは何のためにあるのでしょうか?
まず、Beanの初期化の手順を理解する必要があります
Beanの初期化
singletonObjects
:完全に初期化されたオブジェクトはセカンダリマップに保存されます
earlySingletonObjects
:オブジェクトの早期インスタンス化は、オブジェクトがメモリ内のスペースを開いたが、内部のプロパティを埋めていないことを意味します。このキャッシュは、循環依存関係を解決するために使用される第2レベルのキャッシュです。
singletonFactories
:属性が入力された後、初期化の前。早期公開が許可されている場合、インスタンス化されたBeanがこのキャッシュに追加されます。これを第3レベルのキャッシュと呼びます。
では、インスタンスはどのようにしてキャッシュから取得されますか?
-
最初
singletonObjects
にインスタンスを取得します。singletonObjects
のインスタンスはすべて準備されたBeanインスタンスであり、直接使用できます。 -
取得したインスタンスがnullであるか作成中の場合は、セカンダリキャッシュに移動して取得します。
-
第2レベルのキャッシュにない場合は、第3レベルのキャッシュから取得します。
-
第3レベルのキャッシュにある場合は、第2レベルのキャッシュに移動します。
-
第3レベルのキャッシュが使用できない場合、nullが直接返されます。
Object sharedInstance = getSingleton(beanName);
第3レベルのキャッシュがnullを返す場合、sharedInstance
値はnullです。この時点でBeanが作成されます。
Beanの作成方法は次のとおりです。
createBean(beanName, mbd, args);
createBean()
このメソッドは次のメソッドを呼び出します
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
内部で実行されます
instanceWrapper = createBeanInstance(beanName, mbd, args);
次の呼び出しはaddSingletonFactory
、BeanをsingletonFactories
キャッシュ(3レベルキャッシュ)に追加することです。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
次に実行populateBean()
populateBean(beanName, mbd, instanceWrapper);
populateBean()
主に初期のBeanのプロパティを入力することです。プロパティを入力するときにBeanAがBeanBに依存する必要があることがわかった場合、Bオブジェクトが作成されます。このとき、Bは属性値を入力するときにAに依存する必要があり、属性値をまだ入力していないBeanAを第3レベルのキャッシュから取得することがわかりました。このとき、Aオブジェクトの元の参照をBオブジェクトに挿入して(そして第2レベルのキャッシュに移動して)、循環依存の問題を解決できます。このとき、 getObject()
メソッドの実行が終了しても、完全にインスタンス化されたBeanが返されます。
最後に、完全にインスタンス化されたBeanオブジェクトが第1レベルのキャッシュに格納されますsingletonObjects
。
Beanを取得するためのフローチャートを見てみましょう。
循環依存関係解決図
上記のことから、シーケンシャルループの依存関係がある場合、早期に公開されたBeanは、第3レベルのキャッシュから取得され、第2レベルのキャッシュに移動されることがわかります。
セカンダリキャッシュのみが循環依存の問題を解決できますか?
現時点では、疑問があります。なぜ3レベルのキャッシュが必要なのですか。セカンダリキャッシュは循環依存関係の問題を解決できますか?
このコードを見てみましょう
取得したオブジェクトがプロキシオブジェクトの場合、ここでsingletonFactory.getObject()
新しいプロキシオブジェクトが生成されます。プロキシオブジェクトが1つだけであることを確認するには、プロキシオブジェクトをキャッシュするためのキャッシュが必要です。
では、記事の冒頭の質問に戻ると、@Async
Springが注釈付きメソッドを使用してBeanの循環依存関係の問題を解決しないのはなぜですか?
実際@Async
、プロキシはプロキシオブジェクトの初期参照を提供しないため、デフォルトでは循環参照へのサポートをサポートしていません。@Async
でプロキシオブジェクトはgetEarlyBeanReference()
作成されませんがpostProcessAfterInitialization
、プロキシは作成されます。
次に@Transactional
、注釈の実装に@Async
一貫性があるかどうか、および循環依存の問題があるかどうか疑問に思うかもしれません。@Transactional
自動プロキシクリエーターが使用されているため、答えはノーです。これは、循環依存関係を適切にサポートするメソッドをAbstractAutoProxyCreator
実装getEarlyBeanReference()
します。
また@Async
、@Validated
同じ問題が発生します。
過去に推奨
QRコードをスキャンして、よりエキサイティングになります。または、WeChatでLvshen_9を検索すると、返信してバックグラウンドで情報を取得できます
1.回复"java" 获取java电子书;
2.回复"python"获取python电子书;
3.回复"算法"获取算法电子书;
4.回复"大数据"获取大数据电子书;
5.回复"spring"获取SpringBoot的学习视频。
6.回复"面试"获取一线大厂面试资料
7.回复"进阶之路"获取Java进阶之路的思维导图
8.回复"手册"获取阿里巴巴Java开发手册(嵩山终极版)
9.回复"总结"获取Java后端面试经验总结PDF版
10.回复"Redis"获取Redis命令手册,和Redis专项面试习题(PDF)
11.回复"并发导图"获取Java并发编程思维导图(xmind终极版)
もう1つ:[マイベネフィット]をクリックして、さらに驚きを持ってください。