Springでの循環依存


回答:


42

他の答えが言ったように、Springはそれを処理し、Beanを作成し、必要に応じてそれらを注入します。

結果の1つは、Bean注入/プロパティ設定が、XMLワイヤリングファイルが示唆するように見える順序とは異なる順序で発生する可能性があることです。したがって、プロパティセッターは、既に呼び出されている他のセッターに依存する初期化を行わないように注意する必要があります。これに対処する方法は、InitializingBeanインターフェースを実装するものとしてBeanを宣言することです。これにはメソッドを実装する必要afterPropertiesSet()があり、ここで重要な初期化を行います。(重要なプロパティが実際に設定されていることを確認するためのコードも含めます。)


76

春のリファレンスマニュアルでは、円形の依存関係が解決される方法を説明します。豆は最初にインスタンス化され、次に相互に注入されます。

このクラスを考えてみましょう:

package mypackage;

public class A {

    public A() {
        System.out.println("Creating instance of A");
    }

    private B b;

    public void setB(B b) {
        System.out.println("Setting property b of A instance");
        this.b = b;
    }

}

そして同様のクラスB

package mypackage;

public class B {

    public B() {
        System.out.println("Creating instance of B");
    }

    private A a;

    public void setA(A a) {
        System.out.println("Setting property a of B instance");
        this.a = a;
    }

}

この構成ファイルがある場合:

<bean id="a" class="mypackage.A">
    <property name="b" ref="b" />
</bean>

<bean id="b" class="mypackage.B">
    <property name="a" ref="a" />
</bean>

この構成を使用してコンテキストを作成すると、次の出力が表示されます。

Creating instance of A
Creating instance of B
Setting property a of B instance
Setting property b of A instance

aがに注入されたb場合、aまだ完全に初期化されていないことに注意してください。


26
これが、Springが引数なしのコンストラクタを必要とする理由です;-)
Chris Thompson

15
Beanの定義でコンストラクター引数を使用する場合は違います!(しかし、その場合、循環依存はできません。)
Richard Fearn

1
@Richard Fearnソリューションの提供ではなく、問題の説明についての投稿ですか?
gstackoverflow 2016

4
コンストラクターインジェクションを使用しようとすると、エラーメッセージはorg.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
X. Wo Satuk

19

私が使用しているコードベース(100万行以上のコード)では、起動時間が長く、約60秒で問題がありました。12000以上のFactoryBeanNotInitializedExceptionが発生していました

私がしたことは、AbstractBeanFactory#doGetBeanに条件付きブレークポイントを設定することでした

catch (BeansException ex) {
   // Explicitly remove instance from singleton cache: It might have been put there
   // eagerly by the creation process, to allow for circular reference resolution.
   // Also remove any beans that received a temporary reference to the bean.
   destroySingleton(beanName);
   throw ex;
}

ここでdestroySingleton(beanName)、条件付きブレークポイントコードで例外を出力しました。

   System.out.println(ex);
   return false;

どうやらこれは、FactoryBeanが循環依存グラフに関与しているときに発生します。ApplicationContextAwareInitializingBeanを実装し、手動でBeanを挿入することで解決しました。

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class A implements ApplicationContextAware, InitializingBean{

    private B cyclicDepenency;
    private ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        ctx = applicationContext;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        cyclicDepenency = ctx.getBean(B.class);
    }

    public void useCyclicDependency()
    {
        cyclicDepenency.doSomething();
    }
}

これにより、起動時間が約15秒に短縮されました。

したがって、春がこれらの参照を解決するのに適しているとは必ずしも想定しないでください。

このため、今後の多くの問題を防ぐために、AbstractRefreshableApplicationContext#setAllowCircularReferences(false)で循環依存関係の解決を無効にすることをお勧めします。


3
興味深い推薦。私の反対の推奨事項は、循環参照がパフォーマンスの問題を引き起こしていると疑われる場合にのみ行うことです。(修正する必要のない問題を修正しようとして、修正する必要のないものを修正するのは残念です。)
Stephen C

2
これは循環依存関係を可能にするメンテナンス地獄への滑りやすい傾斜であり、循環依存関係からアーキテクチャを再設計することは、私たちの場合のように本当にトリッキーになる可能性があります。大まかに言えば、セッションファクトリーが循環依存関係に関与していたため、起動時にデータベース接続が2倍になったということです。他のシナリオでは、Beanが12000回以上インスタンス化されたために、さらに悲惨な事態が発生した可能性があります。確かに、破壊をサポートするようにBeanを書く必要がありますが、そもそもなぜこの動作を許可するのでしょうか。
jontejj 2013

@jontejj、あなたはクッキーに値します
serprime 2017年

14

問題->

Class A {
    private final B b; // must initialize in ctor/instance block
    public A(B b) { this.b = b };
}


Class B {
    private final A a; // must initialize in ctor/instance block
    public B(A a) { this.a = a };
 }

//原因:org.springframework.beans.factory.BeanCurrentlyInCreationException:「A」という名前のBeanの作成エラー:要求されたBeanは現在作成中です:解決できない循環参照はありますか?

ソリューション1->

Class A {
    private B b; 
    public A( ) {  };
    //getter-setter for B b
}

Class B {
    private A a;
    public B( ) {  };
    //getter-setter for A a
}

ソリューション2->

Class A {
    private final B b; // must initialize in ctor/instance block
    public A(@Lazy B b) { this.b = b };
}

Class B {
    private final A a; // must initialize in ctor/instance block
    public B(A a) { this.a = a };
}

12

それだけです。aandをインスタンス化しb、それぞれを(setterメソッドを使用して)他のオブジェクトに注入します。

どうしたの?


9
@javaguy:いいえ、ありません。
skaffman

@skaffmanは、propertySetメソッドの使用法を適切に使用する唯一の方法ですか?
gstackoverflow 2016

6

春のリファレンスから:

通常、Springが正しいことを行うと信頼できます。コンテナーのロード時に、存在しないBeanへの参照や循環依存関係などの構成の問題を検出します。Springは、Beanが実際に作成されたときに、できるだけ遅くプロパティを設定し、依存関係を解決します。


6

Springコンテナは、セッターベースの循環依存関係を解決できますが、コンストラクタベースの循環依存関係の場合は、ランタイム例外BeanCurrentlyInCreationExceptionが発生します。Setterベースの循環依存関係の場合、IOCコンテナーは、協調型Beanを挿入する前にそれを完全に構成する通常のシナリオとは異なる方法で処理します。たとえば、Bean AがBean Bに依存し、Bean CのBean Bに依存している場合、コンテナはCを完全に初期化してからBに注入し、Bが完全に初期化されると、Aに注入されます。ただし、循環依存の場合は、1つ完全に初期化される前に、他のBeanに注入されます。


5

AがBに依存しているとすると、SpringはまずAをインスタンス化し、次にBをインスタンス化してから、Bのプロパティを設定し、次にBをAに設定します。

しかし、BもAに依存している場合はどうでしょうか。

私の理解は次のとおりです:SpringはAが構築された(コンストラクターが実行された)が、完全に初期化されていない(すべての注入が行われたわけではない)ことを発見しました。現時点では、完全に初期化されたAインスタンスをBに変換します。Bが完全に初期化された後、Aに設定され、最後にAが完全に開始されました。

つまり、事前にAをBに公開するだけです。

コンストラクターを介した依存関係の場合、SprintはBeanCurrentlyInCreationExceptionをスローするだけです。この例外を解決するには、コンストラクター引数を介して他に依存するBeanのlazy-initをtrueに設定します。


シンプルで最高の説明の1つ。
Sritam Jagadev

5

その明確な説明はこちら。Eugen Paraschivに感謝します。

循環依存関係は設計の匂いです。修正するか、依存関係に@Lazyを使用して問題を回避してください。



3

Spring Bean間にCircular Dependencyがあると、コンストラクターの注入が失敗します。したがって、この場合、セッター注入は問題の解決に役立ちます。

基本的に、コンストラクター注入は必須の依存関係に役立ちます。オプションの依存関係は、再注入を実行できるため、セッター注入を使用する方が適しています。


0

2つのBeanが相互に依存している場合、両方のBean定義でコンストラクター注入を使用しないでください。代わりに、いずれかのBeanでセッター注入を使用する必要があります。(もちろん、両方のBean定義でセッターインジェクションを使用できますが、両方のコンストラクターインジェクションは 'BeanCurrentlyInCreationException'をスローします

https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource」にあるSpringドキュメントを参照してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.