Beanのスコープとは?シングルトンとの関係をやさしく解説【初心者向け超入門】
新人
「先輩、SpringのBeanってよく聞きますけど、具体的に何を指しているんですか?」
先輩
「SpringにおけるBeanは、Springコンテナによって管理されるオブジェクトのことを指すんだ。開発で使うクラスは、ほとんどがBeanとして使われるよ。」
新人
「Beanって、インスタンスを作ってくれる仕組みのことなんですね。じゃあ、Beanのスコープってなんですか?」
先輩
「それはすごく重要なポイントだね。まずはBeanの基本から説明していこうか。」
1. Beanとは何か
Spring Beanとは、Springフレームワークで管理されるJavaオブジェクトのことです。開発者が@Componentや@Service、@Repository、@Controllerなどのアノテーションを付けることで、そのクラスがBeanとして登録され、Springコンテナがインスタンスを生成・管理してくれます。
たとえば、下記のようなGreetingServiceクラスを@Serviceでマークすると、それは自動的にBeanとして登録されます。
package com.example.service;
import org.springframework.stereotype.Service;
@Service
public class GreetingService {
public String getGreeting() {
return "こんにちは、Springの世界へようこそ!";
}
}
このように@Serviceを使うことで、Spring BootはGreetingServiceをBeanとして登録し、自動的にインスタンス化し、必要な場所に注入してくれるようになります。
Beanの定義はSpringの中心的な概念であり、依存性の注入(DI)やライフサイクル管理、テストの簡略化など多くのメリットを提供します。
2. Beanの生成と管理の仕組み
Bean スコープ 初心者が理解すべきことは、SpringがどのようにしてBeanを生成・管理しているかということです。Springはアプリケーションの起動時に「コンテナ」と呼ばれる仕組みを起動し、その中でBeanを一元的に管理します。
開発者が定義したBeanは、Springが自動的に検出してインスタンス化し、必要な箇所に@Autowiredやコンストラクタなどで注入されます。
次のコードは、コントローラーでGreetingServiceを注入して使う例です。
package com.example.controller;
import com.example.service.GreetingService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HelloController {
private final GreetingService greetingService;
public HelloController(GreetingService greetingService) {
this.greetingService = greetingService;
}
@GetMapping("/hello")
public String hello(Model model) {
String message = greetingService.getGreeting();
model.addAttribute("message", message);
return "hello";
}
}
このように@Controllerとコンストラクタを使ってBeanを受け取ると、Springが自動的にGreetingServiceのインスタンスを生成し、渡してくれます。これがDI(依存性注入)と呼ばれる仕組みです。
このGreetingServiceのインスタンスが、アプリケーション全体でどのように共有されるかを決めるのが、次のトピック「Beanのスコープ」です。
たとえば、スコープがsingletonであれば、1つのインスタンスが全体で使われます。逆にprototypeであれば、必要なたびに新しいインスタンスが生成されます。これによって、アプリケーションの振る舞いが大きく変わることになります。
3. Beanのスコープとは?
Bean スコープ シングルトンとは、Springコンテナが管理するBeanの「有効範囲」を意味します。つまり、Beanがどのタイミングで生成され、どのくらいの期間使われるのかを制御する設定のことです。
Springでは、いくつかのスコープが用意されていますが、初心者の方が最初に覚えるべきは以下の2つです。
- singleton(シングルトン):アプリケーション全体で1つのインスタンスを共有
- prototype(プロトタイプ):Beanをリクエストするたびに新しいインスタンスを生成
スコープの指定は、クラスに付けるアノテーションで行います。たとえば、次のように@Scopeアノテーションを使います。
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service
@Scope("prototype")
public class GreetingService {
public String getMessage() {
return "こんにちは(プロトタイプ)";
}
}
このように@Scopeを使えば、Beanの生成と管理の仕方を柔軟に切り替えることができます。
4. シングルトンスコープとは何か
Spring Beanとはというテーマで最もよく使われるのが、singleton(シングルトン)スコープです。これはSpringのデフォルト設定であり、特別に@Scopeを指定しなくても、自動的にシングルトンになります。
シングルトンスコープでは、Beanは1度だけ生成され、アプリケーション全体で同じインスタンスが共有されます。これはメモリの効率が良く、処理が安定しやすいというメリットがあります。
次のコードは、特にスコープを明示していないシンプルなGreetingServiceです。
package com.example.service;
import org.springframework.stereotype.Service;
@Service
public class GreetingService {
private int count = 0;
public String getMessage() {
count++;
return "呼び出し回数:" + count;
}
}
このサービスを何度呼び出しても、インスタンスは1つだけなので、countの値は増え続けます。
@Controller
public class HelloController {
private final GreetingService greetingService;
public HelloController(GreetingService greetingService) {
this.greetingService = greetingService;
}
@GetMapping("/count")
public String count(Model model) {
model.addAttribute("message", greetingService.getMessage());
return "count";
}
}
上記のように設定すると、アクセスするたびにカウントが増えるのが確認できます。
ただし注意点として、状態を持つBeanをシングルトンにすると、複数のリクエストで値が共有されるため、思わぬ不具合を起こすことがあります。そういった場合は、別のスコープに切り替える必要があります。
5. プロトタイプスコープとの比較
Spring プロトタイプスコープ 違いという観点で見ると、プロトタイプスコープはシングルトンとは正反対の動作をします。
プロトタイプスコープでは、Beanを使うたびに新しいインスタンスが生成されます。つまり、1回の呼び出しごとに新しいオブジェクトが作られるため、状態が他と共有されません。
次のコードでは、GreetingServiceをプロトタイプスコープで定義しています。
package com.example.service;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service
@Scope("prototype")
public class GreetingService {
private int count = 0;
public String getMessage() {
count++;
return "呼び出し回数(プロトタイプ):" + count;
}
}
これを呼び出すたびに新しいオブジェクトが作られるため、毎回countは1からスタートします。
このように、状態を持つ処理を安全に実行したい場合や、個別の処理単位でロジックを分離したい場合は、プロトタイプスコープが向いています。
使い分けの具体例
- シングルトン:設定値の提供クラスや、外部API接続クラスのように状態を持たない処理に適している
- プロトタイプ:フォーム入力の一時的なデータ保持や、ユーザーごとのセッションごとに異なる処理が必要なケースに向いている
Springのスコープは、アプリケーションの設計に大きな影響を与える重要な要素です。Bean スコープ シングルトンの基本をしっかり理解し、必要に応じてprototypeやその他のスコープを適切に使い分けることで、より安定したアプリケーション開発が実現できます。
6. よくあるトラブルとその対処
DI スコープ 設定ミスに関するトラブルは、Springの開発で初心者が最もつまずきやすいポイントのひとつです。特に、スコープが異なるBean同士を注入しようとした場合、予期しない動作になることがあります。
たとえば、singletonスコープのクラスに、prototypeスコープのクラスを@Autowiredで注入した場合、最初の1回だけインスタンスが生成され、それ以降は同じインスタンスが使われてしまいます。
次のコードは、スコープの不一致による誤動作の例です。
@Service
@Scope("prototype")
public class CounterService {
private int count = 0;
public int increment() {
return ++count;
}
}
@Service
public class MainService {
private final CounterService counterService;
public MainService(CounterService counterService) {
this.counterService = counterService;
}
public int getCount() {
return counterService.increment();
}
}
この場合、MainServiceはsingletonスコープであるため、CounterServiceのインスタンスも最初の1回だけ生成され、それ以降は同じオブジェクトを使い回してしまいます。これではprototypeの意味がなくなってしまいます。
このようなスコープの違いによるトラブルを回避するには、ApplicationContextを使って明示的にprototypeのBeanを取得する方法があります。
@Service
public class MainService {
private final ApplicationContext context;
public MainService(ApplicationContext context) {
this.context = context;
}
public int getCount() {
CounterService counter = context.getBean(CounterService.class);
return counter.increment();
}
}
このようにすることで、毎回新しいインスタンスを取得できるようになり、本来のprototypeの動作になります。
また、セッションスコープやリクエストスコープのようなWeb特有のスコープでも同様の問題が発生することがあります。そのため、Beanのスコープは必ず意識して設計しましょう。
7. 実際の開発現場でのスコープの使い分け例
Spring Bean シングルトン 実例として、よくある業務システムを考えてみましょう。たとえば「ユーザー管理システム」のようなアプリケーションでは、以下のようにスコープを使い分けます。
■ 設定サービス(シングルトン)
アプリケーション全体で共通の設定値や外部サービスの接続情報を保持するサービスは、singletonスコープが適しています。
@Service
public class AppConfigService {
public String getApiEndpoint() {
return "https://api.example.com";
}
}
このようなクラスは一度だけ生成され、全体で使いまわすことで効率化が図れます。
■ 入力フォームの一時データ保持(プロトタイプ)
ユーザーが何かの入力フォームを開いたとき、一時的にデータを保持するクラスはprototypeが適しています。
@Component
@Scope("prototype")
public class FormData {
private String name;
private String email;
// getter, setter
}
このクラスは、リクエストごとに別のインスタンスが必要になるため、prototypeにしておくと安全です。
■ コントローラーの注入先(状態を持たない)
@Controllerで定義されるクラスは基本的にsingletonで構いません。ステートレス(状態を持たない)設計であれば、問題なく動作します。
ただし、prototypeのようなBeanを内部で利用する場合は、先ほどのようにApplicationContextで都度取得する必要があります。
8. まとめとして、初心者が意識すべきBeanスコープのポイント
ここまでBeanのスコープについて学んできましたが、初心者がまず意識すべきポイントを以下に整理します。
- Spring Beanは、Springコンテナによって生成・管理されるJavaオブジェクトです。
- スコープはBeanの生存期間や共有範囲を決定する設定です。
- デフォルトはsingletonで、全体で1つのインスタンスを共有します。
- prototypeは、呼び出しのたびに新しいインスタンスが作られます。
- DI スコープ 設定ミスを防ぐために、スコープの違いに注意して設計することが重要です。
- 状態を持たないBeanはsingleton、状態を持つBeanはprototypeなど、用途に応じて使い分けましょう。
初心者の方はまず、singletonとprototypeの違いを理解し、どちらが自分の目的に合っているかを判断できるようになることが大切です。そして、開発中に意図しない動作が発生したときは、まずスコープの設定を見直してみましょう。