導入
OrderService のBeanを、注文処理のたびに毎回作り直しているのでしょうか。それとも、アプリ起動中ずっと同じインスタンスを使い回しているのでしょうか。この疑問に答えるのが スコープ(scope) です。
説明
Springの既定のスコープは singleton です。これは「DIコンテナの中に、そのBeanのインスタンスは1つだけ」という意味です。
graph TD Ctx["ApplicationContext"] -->|1つだけ保持| Bean["OrderService のインスタンス"] R1["リクエスト1"] -->|同じインスタンスを使う| Bean R2["リクエスト2"] -->|同じインスタンスを使う| Bean R3["リクエスト3"] -->|同じインスタンスを使う| Bean
何百回リクエストが来ても、OrderService のインスタンスは使い回されます。だからこそレッスン4で見た「final フィールドで状態を持たない(=リクエストごとに変わる可変フィールドを持たない)」設計が重要になります。同じインスタンスを複数のリクエストが同時に使うため、可変な状態を持つと予期しないバグの原因になるからです。
もう1つのスコープが prototype です。context.getBean(...) や注入のたびに、新しいインスタンスが作られます。
@Component
@Scope("prototype")
class ReportBuilder {
private final List<String> lines = new ArrayList<>(); // 呼ぶたびに空の状態から始めたい
public void addLine(String line) {
lines.add(line);
}
}
| スコープ | インスタンス数 | 主な用途 |
|---|---|---|
| singleton(既定) | コンテナに1つ | ほとんどのBean(Service, Repository, Controllerなど) |
| prototype | 呼ばれるたびに新規 | 内部に状態を持つ使い捨てのオブジェクト |
実務では大半のBeanがsingletonのままで問題ありません。prototypeを使うのは、Beanの中に「呼び出しごとに異なる状態」を持たせたい特殊なケースに限られます。
やってみよう
OrderService に、うっかり private List<String> processedOrders = new ArrayList<>(); のような可変フィールドを追加してしまったとします。singletonスコープだと、これがどんな不具合を引き起こすか考えてみましょう(ヒント: 複数のリクエストが同時にこのフィールドへ追記したら?)。