Spring Bootにおける@Configuration / @Beanって何? 使い方を分かりやすく解説
皆さんはSpring Bootで開発していて、@Configuration や @Bean を見かけたことはありますでしょうか?
「@Component と何が違うの?」「どこで使うのが正解?」と混乱しがちなポイントでもあります。
この記事では、@Configuration / @Bean の役割と使い方を、具体例を交えて解説します。
@Configurationとは?
@Configuration は「このクラスは設定(Configuration)クラスですよ」とSpringに伝えるアノテーションです。
設定クラスの中で @Bean メソッドを定義し、Springコンテナに登録したいオブジェクト(Bean)を組み立てます。
イメージとしては、「Beanをどう作るかをまとめて書く場所」を用意するのが @Configuration です。
@Beanとは?
@Bean は メソッドに付ける アノテーションで、その 戻り値をSpringコンテナにBeanとして登録します。
@Component が「クラスに付けて、クラス自体をBean登録する」のに対し、@Bean は「メソッドが返したオブジェクトをBean登録する」点が大きな違いです。
@Bean が活躍する典型例は以下です。
- 自分で作っていない(外部ライブラリの)クラスをBean登録したい
- 生成手順が少し複雑で、newするだけでは終わらない
- 生成時に設定値や依存Beanを組み合わせたい
基本的な使い方(サンプル)
例えば、外部ライブラリのクラス Clock をBeanとして登録したいとします。クラスに @Component を付けられないので、@Bean の出番です。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Clock;
@Configuration
public class AppConfig {
@Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
}
これで Clock がSpringコンテナに登録され、他のクラスからDIできます。
import org.springframework.stereotype.Service;
import java.time.Clock;
@Service
public class TimeService {
private final Clock clock;
public TimeService(Clock clock) {
this.clock = clock;
}
}
よく使うテクニック
Bean名を指定する
デフォルトでは メソッド名がBean名 になります。
明示したい場合は以下のように指定できます。
@Bean("systemClock")
public Clock clock() {
return Clock.systemDefaultZone();
}
@Beanメソッドの引数で依存を受け取る(これが便利)
@Bean メソッドは、引数に必要なBeanを書くだけでSpringが解決して渡してくれます。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public MyClient myClient(MyProperties props, Clock clock) {
return new MyClient(props.getEndpoint(), clock);
}
}
「Beanを組み立てる場所」として @Configuration を使う旨味が出るポイントです。
スコープを変える(必要なときだけ)
基本はsingleton(アプリ内で1つ)ですが、用途によってはprototypeなどを使うこともあります。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
@Bean
@Scope("prototype")
public SomeObject someObject() {
return new SomeObject();
}
(とはいえ、通常の業務アプリではsingletonがほとんどです)
ハマりやすいポイント
1) @Configurationの「プロキシ」による挙動差
@Configuration が付いたクラスは、内部的にプロキシ化されることで、@Bean メソッド同士の呼び出しが「同一Bean」を返すように調整されます。
そのため、以下のようなコードでも a() が毎回newされる…ではなく、基本的にはコンテナ管理の同一インスタンスが返ります。
@Configuration
public class AppConfig {
@Bean
public A a() {
return new A();
}
@Bean
public B b() {
return new B(a()); // a() を呼んでいる
}
}
逆に言うと、@Configuration を付けない(または設定次第)だと、このあたりの挙動が変わって混乱の元になります。
最初のうちは 「@Beanを書くなら素直に@Configurationに寄せる」 と覚えておくと事故が減ります。
2) @Configurationが無くても@Beanは使える?
結論から言うと 使えます。ただし前提があります。
前提:そのクラス自体がSpringの管理下に入っていること
@Bean は「Springが拾ってくれるクラス」の中に書かれて初めて意味を持ちます。
つまり、以下のように クラスそのものがBeanとして登録される状況なら、@Configuration が無くても @Bean は動きます。
@Componentが付いている@SpringBootApplication配下でコンポーネントスキャンされる(または@Importされる)- Java Configとして明示的に読み込まれている
例:@Component でも @Bean は機能します。
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.time.Clock;
@Component
public class AppConfig {
@Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
}
この場合も Clock はSpringコンテナにBean登録されます。
ただし、@Configurationがあると安全
@Configuration が付いていると、設定クラスが「フルモード(CGLIBプロキシ)」になり、@Bean メソッド同士の呼び出しがあっても コンテナ管理の同一Bean が返りやすくなります。
逆に @Component などで @Configuration なしの場合、@Bean メソッドを ただのメソッド呼び出しとして扱ってしまい、状況によっては 別インスタンスが生成されることがあります。
そのため、設定クラスとして書くなら 基本は @Configuration を付けるのが無難です。
3) @Componentで済むものを無理に@Beanで書かない
自分のクラスで、生成も単純で、普通にコンポーネントスキャン対象にできるなら @Component / @Service / @Repository などで十分です。
@Bean は「クラスにアノテーションを付けられない」「生成が少し特殊」なときに効きます。
まとめ
@Configurationは「Beanの作り方をまとめて書く設定クラス」@Beanは「メソッドの戻り値をSpringコンテナにBean登録する」- 外部ライブラリのクラスをDIしたい時や、生成が複雑な時に
@Beanが特に便利 - @Configurationが無くても@Beanは使える(ただし、そのクラスがSpring管理下にあることが前提)
- ただし、設定クラスとして書くなら @Configurationを付けて運用するのが事故りにくい