互いに自動配線するSpringBeanのグラフがあります。非常に簡略化された図:
_<context:annotation-config/>
<bean class="Foo"/>
<bean class="Bar"/>
<bean class="Baz"/>
...
public class Foo {
@Autowired Bar bar;
@Autowired Baz baz;
}
public class Bar {
@Autowired Foo foo;
}
public class Baz {
@Autowired Foo foo;
}
_
これらのBeanはすべて、シングルトンであることを意味するスコープが指定されていません(明示的なシングルトンにしても、何も変更されません。私は試しました)。
問題は、単一のアプリケーションコンテキストのインスタンス化後、Bar
とBaz
のインスタンスにFoo
の異なるインスタンス。これはどのように起こりますか?
Foo
のpublicno argsコンストラクターを作成しようとしましたが、デバッグにより、Foo
が複数回作成されていることが確認されました。これらすべての作成のスタックトレースは ここ です。
また、Springのデバッグログを有効にしようとしましたが、他のすべての行の中で、次のようになりました。
_DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'Foo'
DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'Foo'
DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'Foo'
_
私のBeanが相互参照していることは理解していますが、Springフレームワークがシングルトンスコープを尊重し、シングルトンBeanを一度初期化してから、必要な人に自動配線することを期待しています。
古い学校のprivate
コンストラクターを_public static Foo getInstance
_アクセサーと一緒に使用すると、これは問題なく機能します。コンテキストのセットアップ中に例外はスローされません。
FWIW、私はo.s.c.s.ClassPathXmlApplicationContext(String ...configLocations)
コンストラクターでSpringバージョン3.0.5(3.1.2でも試して、同じ結果)を使用しています。
静的初期化子を使用するようにコードを簡単に変換できますが、Springがこのように動作する理由を理解したいと思います。これはバグですか?
編集:いくつかの追加調査により、
context.getBean(Foo.class)
alwaysへの後続のすべてのリクエストは、Foo
の同じインスタンスを返します。@Autowired
_をセッター(このBeanの約20回の使用)に置き換えても、このオブジェクトは複数構築されますが、すべての依存関係には同じが注入されます参照。上記の私には、これは_@Autowired
_の実装に関連するSpringのバグであることが示唆されています。 Springコミュニティフォーラムに投稿し、何か役立つものがあればここに投稿します。
Context:component-scanアノテーションに注意しない場合、子コンテキストは同じシングルトンBeanを再インスタンス化できます(MVCアノテーションなどの他のSpringコンテキストスキャンアノテーションもあります)。これは、WebアプリケーションでSpringサーブレットを使用する場合の一般的な問題です。 DispatcherServletが別のアプリケーションコンテキストを作成する理由 を参照してください。
子コンテキストでコンポーネントを再スキャンしていないこと、または特定のパッケージ/注釈のみをスキャンして、ルートコンテキストコンポーネントスキャンからそのパッケージ/注釈を除外していることを確認してください。
何らかの理由で、統合テストとサービスでもこれがランダムにポップアップします(春のバージョン4.1.4、Java 1.8)。
複数の原因があるようです-最初は自動配線が原因であるように見えました。
ただし、影響を受ける各Beanに「id」フィールドを指定することで、最も一貫性のある障害を解決しました。
私のSpring構成は次のようでした:
<context:annotation-config/>
<bean class="Bar" />
<bean class="Foo" />
<bean class="Baz" />
クラスはあなたと同じです
次のようなテストアプリ:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("META-INF/spring/testctx.xml");
Foo foo = ctx.getBean(Foo.class);
Baz baz = ctx.getBean(Baz.class);
Bar bar = ctx.getBean(Bar.class);
System.out.println(foo.equals(baz.foo));
System.out.println(foo.equals(bar.foo));
System.out.println(baz.equals(foo.baz));
System.out.println(foo.baz.toString());
System.out.println(baz.toString());
System.out.println(foo.bar.toString());
System.out.println(bar.toString());
}
}
次のようなテストアプリからの出力:
true
true
true
Baz@8aef2b
Baz@8aef2b
Bar@215bf054
Bar@215bf054
3.0.6を使用すると、完全に正常に機能します(シングルトンBeanは確かにシングルトンです)。ここで説明しなかったことが、構成を台無しにしている可能性があります。もちろん、補足として、デフォルトのパッケージを使用すると、いくつかの不思議な魔法が発生する可能性があります;-)
コンストラクターの代わりにセッターインジェクションを使用して、それが機能するかどうかを確認してください。春のBean xmlで、BeanAからBeanBへの参照を指定します。その逆も同様です。