こんにちは、ASKULのユウです。
Spring Bootで2.0系から2.2系へバージョンアップしたときにハマりました。
ComponentScanでscanされる順序が2.0系と2.1系〜で異なっていることが原因でした。
この辺の情報はドキュメントにも載っていなかったため、かなり困惑しました。
その他要因もあり1週間ぐらいハマっていたので、備忘録的に残しておきます。
前提
- Spring Boot 2.0.3 → Spring Boot 2.2.2
- Apache camel 2.24.1
- log4j2
経緯
Spring Bootのバージョン2.0.3と古かったため、2.2系に更新しようと試みる。
しかし、更新後に実行してみても、アプリケーションの起動に失敗してしまう状況だった。
原因の確認
エラー内容を確認すると、そんなbeanが存在しないというログが出力されている。
具体的には、Apache camel上のtransactedで指定しているbeanが見つからないという内容のエラーだった。
※次のログのクラス名などは加工しています。
org.apache.camel.RuntimeCamelException: org.apache.camel.FailedToCreateRouteException: Failed to create route xxxxRoute at: >>> Transacted[ref:dbTxManagerPolicy] <<< in route: Route(xxxxRoute)[[From[direct:so... because of No bean could be found in the registry for: dbTxManagerPolicy of type: org.apache.camel.spi.Policy
Spring Bootのトレースログが出力されるように変更する。
// log4j2.xml <Logger name="org.springframework.boot.autoconfigure" level="trace" additivity="false"> <AppenderRef ref="xxxxx" /> </Logger>
トレースログが出力されるようになったらログを確認してみる。
Condition OnBeanCondition on jp.co.askul.xxx.xxxxAutoConfiguration#dbTxManagerPolicy did not match due to @ConditionalOnBean (names: dbMaster; SearchStrategy: all) did not find any beans named 'dbMaster'
dbMaster
というbeanが見つからなくて@ConditionalOnBean
でDIコンテナへのbean登録が除外されている。
(@ConditionalOnBean
は指定されているbeanがDIコンテナに存在しなければ登録しない指定)
ちなみにSpring Boot2.0.3では以下が出力される。
Condition OnBeanCondition on jp.co.askul.xxx.xxxxAutoConfiguration#dbTxManagerPolicy matched due to @ConditionalOnBean (names: dbMaster; SearchStrategy: all) found bean 'dbMaster'
同一のbeanがみつかっている。
上記ログの前後を確認すると、2.0.3と2.2.2で依存関係があるbean登録の順番が変わってしまっているように見えた。
(ドキュメントにはないが、Spring Boot2.0系と2.1系以降を比較すると、ComponentScanされる順番が変更されている模様)
問題の解決
dbTxManagerPolicy
がDIコンテナへ登録される前に、依存関係があるbean登録を行っているクラスを指定するようにする。
@AutoConfigureAfter
でdbMaster
のbean登録を行っている依存クラスを指定する。
@Configuration @AutoConfigureAfter(DbDataSourceAutoConfiguration.class) public class xxxxAutoConfiguration { }
上記アノテーションを付与した後、再度アプリケーションを実行してログ確認してみる。
しかし、ComponentScanされる順番が変更にならない...
@AutoConfigureBefore
や@AutoConfigureOrder
で依存関係を指定してみても変わらない...
しばらく悩んでいたところ、spring.factoriesファイルでAutoConfigurationするクラスを指定しなければならないという情報を得る。
https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-locating-auto-configuration-candidates
このファイルに対象クラスを指定していない場合、@AutoConfigureAfter
をつけても有効にならない模様。
META-INF配下に次のファイルを配置する。
// spring.factories // 複数クラスある場合は「,」区切りでクラスを指定 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ jp.co.askul.xxx.xxxxAutoConfiguration
再度ログを確認したところ、意図したbeanの登録順になりアプリケーション実行が成功した。
最後に
今までSpring Bootはあまり理解せずに利用していた程度だったので、beanの登録の仕組みを知ることができてよい勉強になりました。
トラブル解決はかなり勉強になりますね。
調べても出てこない情報だったので、誰かの役に立てばと思います。