Springで@ConditionalOnBeanを扱うときの注意事項
結論
@ConditionalOnBean, @ConditionalOnMissingBeanはAutoConfigurationクラス以外では使わない方がいいよ!
@ConditionalOnBean, @ConditionalOnMissingBeanとは
特定のBeanが存在するときのみ、もしく存在しないときのみにこれらのアノテーションを付与したBeanを有効化することができるアノテーションです。@ConditionalOnBeanを付与したBeanは特定のBeanがApplicationContextに存在する場合有効化され、逆に@ConditionalOnMissingBeanを付与したBeanは特定のBeanがApplicationContextに存在しない場合に有効化されます。
@Configuration
public class SampleConfig {
@Bean
@ConditionalOnMissingBean // sampleServiceというBeanがApplicationContextに存在しない場合にこのBeanが有効化される
public SampleService sampleService() {
return new SampleService();
}
}
なぜAutoConfigurationクラス以外では使わない方がいいのか
公式ドキュメントにも書いてありますが、@ConditionalOnBean、@ConditionalOnMissingBeanの評価はこれらのアノテーションを付与したBean定義がApplicationContextに追加された時点の情報をもとに行うため、Bean定義の追加順序によっては意図しない結果になる可能性があるからです。
これに気づかず少々ハマってしまいました・・。
@Configuration
@ConditionalOnBean(BConfig.class) // BConfigは定義しているのになぜかAConfigが有効化されないことがある
public class AConfig {
...
}
@Configuration
public class BConfig {
...
}
ではどうするか
他のConditional系のアノテーションで代用できるならそちらで代用すると良いと思います。例えば@ConditionalOnPropertyや@ConditionalOnWebApplicationなどであれば上記の制約はなく使うことが可能です。
-
@ConditionalOnProperty: 特定のプロパティ値がある場合にBeanが有効になる -
@ConditionalOnWebApplication: アプリケーションがWebアプリケーションの場合にBeanが有効になる
参考
では@ConditionalOnBean、@ConditionalOnMissingBeanはどこで使うべきか
それは最初に書いたようにAutoConfigurationなクラスで使うことが推奨されています。AutoConfigurationクラスにこれらのアノテーションを付与する場合は上記の問題は起こりません。
なぜならAutoConfigurationクラスは必ずユーザが定義したBean定義がApplicationContextに追加された後に読み込まれるためです。
その他参考
Bean定義の読み込みやBeanの生成などBeanのライフサイクルについては以前Qiitaにまとめたのでこちらを参照
Discussion