オリジナル:http://www.javastack.cn/article/2020/spring-cycle-depends-on-three-ways/
循環依存は、Nクラスの循環ネスト参照です。この循環依存が日常の開発で新しいオブジェクトの邪魔になる場合、プログラムは実行時にメモリがオーバーフローしてエラーが報告されるまでループで呼び出されます。
Springが循環依存関係を解決する方法について説明します。
最初のタイプ:コンストラクターパラメーターの循環依存
Springコンテナは、作成中の各Bean識別子を「現在作成されているBeanプール」に入れ、Bean識別子は作成プロセス中にこのプールに残ります。
したがって、Beanの作成プロセス中にすでに「現在作成されているBeanプール」にいることが判明した場合、循環依存関係を示すBeanCurrentlyInCreationExceptionがスローされ、作成されたBeanは「現在作成されているBeanプール」から削除されます。
まず、3つのBeanを初期化します。
public class StudentA {
private StudentB studentB ;
public void setStudentB(StudentB studentB) {
this.studentB = studentB;
}
public StudentA() {
}
public StudentA(StudentB studentB) {
this.studentB = studentB;
}
}
public class StudentB {
private StudentC studentC ;
public void setStudentC(StudentC studentC) {
this.studentC = studentC;
}
public StudentB() {
}
public StudentB(StudentC studentC) {
this.studentC = studentC;
}
}
public class StudentC {
private StudentA studentA ;
public void setStudentA(StudentA studentA) {
this.studentA = studentA;
}
public StudentC() {
}
public StudentC(StudentA studentA) {
this.studentA = studentA;
}
}
上記は非常に基本的な3つのクラスであり、StudentAのパラメーター構造はStudentBです。StudentBのパラメーター化された構造はStudentCであり、StudentCのパラメーター化された構造はStudentAであり、循環依存関係を作成します。
私たちはすべて、これら3つのBeanを管理のためにSpringに提供し、パラメーター化された構造でインスタンス化します。
<bean id="a" class="com.zfx.student.StudentA">
<constructor-arg index="0" ref="b"></constructor-arg>
</bean>
<bean id="b" class="com.zfx.student.StudentB">
<constructor-arg index="0" ref="c"></constructor-arg>
</bean>
<bean id="c" class="com.zfx.student.StudentC">
<constructor-arg index="0" ref="a"></constructor-arg>
</bean>
以下はテストクラスです:
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");
//System.out.println(context.getBean("a", StudentA.class));
}
}
実行結果のエラーメッセージは次のとおりです。
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
冒頭の文を理解できれば、このエラーは驚くべきことではありません。Springコンテナは最初にシングルトンStudentAを作成し、StudentAはStudentBに依存し、次にAを「現在作成されているBeanプール」に配置します。
この時点でStudentBが作成され、StudentBはStudentCに依存し、次にBは「現在作成されているBeanプール」に配置され、StudentCはこの時点で作成され、StudentCはStudentAに依存します。
ただし、現時点では、Studentはすでにプール内にあるため、プール内のBeanは初期化されていないため、エラーが報告され、エラーに依存し、初期化されたBeanがプールから削除されます。
2番目のタイプ:セッターモードシングルトン、デフォルトモード
セッターインジェクションについてお話ししたい場合は、SpringでのBeanのインスタンス化の画像をご覧ください。
図の最初の2つのステップに示すように、Springは最初にBeanオブジェクトをインスタンス化してから、オブジェクトのプロパティを設定します。SpringのBeanがデフォルトでシングルトンになるのはなぜですか?この記事をご覧になることをお勧めします。
WeChatパブリックアカウントに注意してください:Javaテクノロジースタック、およびバックグラウンドで返信:春、私がコンパイルしたN個の最新のSpringチュートリアルを入手できます。
セットインジェクションの構成ファイルを変更する
<!--scope="singleton"(默认就是单例方式) -->
<bean id="a" class="com.zfx.student.StudentA" scope="singleton">
<property name="studentB" ref="b"></property>
</bean>
<bean id="b" class="com.zfx.student.StudentB" scope="singleton">
<property name="studentC" ref="c"></property>
</bean>
<bean id="c" class="com.zfx.student.StudentC" scope="singleton">
<property name="studentA" ref="a"></property>
</bean>
以下はテストクラスです:
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");
System.out.println(context.getBean("a", StudentA.class));
}
}
印刷結果は次のとおりです。
com.zfx.student.StudentA@1fbfd6
setメソッドを使用してもエラーが発生しないのはなぜですか?
上記の図に基づいて、Springは最初に構築を使用してBeanオブジェクトをインスタンス化します。このとき、Springはインスタンス化されたオブジェクトをMapに配置し、Springは属性が設定されていないこのインスタンス化されたオブジェクトの参照を取得するメソッドを提供します。 。
この例では、SpringがStudentA、StudentB、StudentCをインスタンス化すると、オブジェクトのプロパティがすぐに設定されます。このとき、StudentAはStudentBに依存しており、マップに移動して、そこに存在するシングルトンのStudentBオブジェクトを取り出します。類推すると、ループの問題はありません。
以下はSpringのソースコードでの実装方法です。次のソースコードは、SpringのBeanパッケージのDefaultSingletonBeanRegistry.javaクラスにあります。
/** Cache of singleton objects: bean name --> bean instance(缓存单例实例化对象的Map集合) */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
/** Cache of singleton factories: bean name --> ObjectFactory(单例的工厂Bean缓存集合) */
private final Map<String, ObjectFactory> singletonFactories = new HashMap<String, ObjectFactory>(16);
/** Cache of early singleton objects: bean name --> bean instance(早期的单身对象缓存集合) */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** Set of registered singletons, containing the bean names in registration order(单例的实例化对象名称集合) */
private final Set<String> registeredSingletons = new LinkedHashSet<String>(64);
/**
* 添加单例实例
* 解决循环引用的问题
* Add the given singleton factory for building the specified singleton
* if necessary.
* <p>To be called for eager registration of singletons, e.g. to be able to
* resolve circular references.
* @param beanName the name of the bean
* @param singletonFactory the factory for the singleton object
*/
protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
3番目のタイプ:セッターモードプロトタイプ、プロトタイプ
構成ファイルを次のように変更します。
<bean id="a" class="com.zfx.student.StudentA" scope="prototype">
<property name="studentB" ref="b"></property>
</bean>
<bean id="b" class="com.zfx.student.StudentB" scope="prototype">
<property name="studentC" ref="c"></property>
</bean>
<bean id="c" class="com.zfx.student.StudentC" scope="prototype">
<property name="studentA" ref="a"></property>
</bean>
scope = "プロトタイプ"は、すべてのリクエストがインスタンスオブジェクトを作成することを意味します。
2つの違いは、ステートフルBeanはプロトタイプスコープを使用し、ステートレスBeanは通常シングルトンスコープを使用します。
テストケース:
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");
//此时必须要获取Spring管理的实例,因为现在scope="prototype" 只有请求获取的时候才会实例化对象
System.out.println(context.getBean("a", StudentA.class));
}
}
印刷結果:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
プロトタイプモードでエラーが報告されたのはなぜですか?
「プロトタイプ」スコープのBeanの場合、「プロトタイプ」スコープのBeanはSpringコンテナーによってキャッシュされないため、作成されたBeanを事前に公開できないため、Springコンテナーは依存性注入を完了できません。