Spring Bootを2.0系から2.2系にバージョンアップしたらハマった

こんにちは、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登録を行っているクラスを指定するようにする。
@AutoConfigureAfterdbMasterの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の登録の仕組みを知ることができてよい勉強になりました。
トラブル解決はかなり勉強になりますね。
調べても出てこない情報だったので、誰かの役に立てばと思います。

ASKUL Engineering BLOG

2020 © ASKUL Corporation. All rights reserved.