本文へスキップ
BecomeCoder

Spring / Spring Bootコース · 第2章 Bean と DIコンテナ ― Springに管理してもらう部品たち · レッスン7

Beanのスコープ ― singletonとprototype

ローカル実施

導入

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スコープだと、これがどんな不具合を引き起こすか考えてみましょう(ヒント: 複数のリクエストが同時にこのフィールドへ追記したら?)。