まずは結論から
SpringのDIコンテナに管理してもらいたいクラスが、自分で作成しているクラスなのか、それとも外部のライブラリ(サードパーティのライブラリ)のクラスなのかによって、@Component
を使うか@Bean
を使うか変わる。
@Componentとは
@Compnent
は、Spring BootでWebのMVCアプリを作成するときに使用する@Controller
, @Service
, @Repository
と同様に、SpringのDIコンテナに管理させて@Autowire
などでDIできるようにしたいクラスにつける。
@Controller
等はMVCの文脈上で特化したクラスにつけられるのに対して、@Component
はそれら以外の特化していないクラス全般に付与するだけで、基本的には同じ。
@Beanとは
@Bean
も同様にSpringのDIコンテナに管理させたいものにつける点は同じ。
@Configuration
をつけたクラスの中で、DIコンテナに管理させたいクラスのインスタンスを作成するメソッドにつける。
@Configuration
public class Config {
@Bean
public SomeClass someClass() {
return new SomeClass();
}
}
@Configuration
@Bean
を使うときに@Configuration
をつけたクラスの中で実施しないといけない理由は、Spring Bootが起動時にDIコンテナに登録する対象を見つける際に@Configuration
をつけたクラスを探すから。
Spring Bootの起動mainクラスに付与する@SpringBootApplication
には@EnableAutoConfiguration
というアノテーションが付けられていて、これによって自動で探してくれる。ここの仕組みはSpring BootのAutoConfigureの仕組みを理解する
の画像がわかりやすい。
出典: Spring BootのAutoConfigureの仕組みを理解する
@ComponentScan
@Component
(や@Controller
など)は、@Configuration
のおかげではなく@ComponentScan
のおかげで、DIコンテナに登録する対象クラスとして探してくれる。
@ComponentScan
もSpring Bootの起動mainクラスに付与する@SpringBootApplication
に付与されている。
@Componentと@Beanの使い分け
どちらもSpringのDIコンテナに管理させたいものにつける点は同じなのであれば、どのように使い分けるのか。
stackoverflowの「Spring: @Component versus @Bean」の回答がわかりやすい。
Sometimes automatic configuration is not an option. When? Let's imagine that you want to wire components from 3rd-party libraries (you don't have the source code so you can't annotate its classes with @Component), so automatic configuration is not possible.
The @Bean annotation returns an object that spring should register as bean in application context. The body of the method bears the logic responsible for creating the instance.
要点
まとめると以下のようになる。
@Component
を使いたくても使えないときがある。それはソースコードに手を入れられないサードパーティライブラリのクラスをDI対象にしたいとき。@Bean
はメソッドでそのクラスを実体化することができるから、そのような場合に使用すべき。
感想
実際に開発していると、確かにその通りの使い分けを自分でしていた。
なんとなく使い分けが上記の通りできていたが、なぜそのように使い分けているのかあまり理解しないまま書いてしまっていた。stackoverflowの回答がわかりやすく、理解が深まった。
サードパーティか否か: Spring Bootのドキュメントを読む
サードパーティか否かで使うアノテーションが変わるという点では、Spring Bootのドキュメントにもう一つ同じ考え方のものが記載されている。
@ConfigurationProperties
@ConfigurationProperties
を使ってapplication.yml等で設定した値をJavaのクラスのフィールドにバインドできる。
参考元: 2.8.3. @ConfigurationProperties アノテーション付きタイプの有効化
@ConfigurationPropertiesScan
@ConfigurationPropertiesScan
を@SpringBootApplication
が付けられたメインアプリケーションクラスに追加すれば、@ConfigurationProperties
をSpring Bootが読み込んでくれる。
@EnableConfigurationProperties
あるいは@EnableConfigurationProperties(特定のPropertiesクラス.class)
を@SpringBootApplication
のクラスか@Configuration
が付与されているクラスに付与すれば、特定のPropertiesクラスの設定値を有効にできる。@ConfigurationPropertiesScan
で一括してスキャンするのが適さない場合に使用する。
@Component
また@Component
を@ConfigurationProperties
とともに使用すれば、フィールドに値をバインドしてくれた上で、通常の@Component
をつけただけのクラスと同様にSpringのDI管理対象として扱ってくれる。
@Beanでサードパーティのライブラリのクラスにバインドする
一方、2.8.5. サードパーティの構成の段落名の通り、サードパーティのライブラリのクラスを使う場合には、そのソースコードに@ConfigurationProperties
を付与できないので、他の方法を取るしかない。
そのサードパーティのクラスの実体を返す@Bean
を付与したメソッドを作ってあげることで、SpringのDIコンテナの管理対象とすることができるが、そのメソッド自体に@ConfigurationProperties
も併せて付与すると、設定値がバインドされたインスタンスを取得できる。
ドキュメントURL
https://spring.pleiades.io/spring-boot/docs/current/reference/html/features.html#features
※URLがcurrentとなっているが、本日時点のSpring Bootのバージョンは2.5.1のため2.5.1に変更したURLも載せておく。
https://spring.pleiades.io/spring-boot/docs/2.5.1/reference/html/features.html#features
また上記は和訳なので、原文の方のURLも載せておく。
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features