まずは結論から

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