Micronautのスコープまとめ!Singleton・Prototype・ThreadLocalを初心者向けに整理解説
生徒
「MicronautでDIを使っていると、SingletonとかPrototypeとか出てきて混乱します……」
先生
「Micronautでは、Beanがどのタイミングで作られて、どれくらい使い回されるかをスコープで決めています」
生徒
「スコープって、メモリの使われ方にも影響するんですか?」
先生
「影響します。特にSingletonとPrototypeの違いを理解すると、DI設計が一気に楽になります」
生徒
「ThreadLocalっていうのも見かけましたが、これは何ですか?」
先生
「では、Micronautのスコープを順番に整理しながら見ていきましょう」
1. Micronautにおけるスコープとは何か
Micronautのスコープとは、DIコンテナが管理するBeanの生成タイミングや生存期間を決める仕組みです。 JavaのDIフレームワークでは、同じクラスでも使い方によって生成回数を制御する必要があります。 Micronautではアノテーションを使うことで、Beanのライフサイクルを明確に指定できます。
特に初心者がつまずきやすいのが、常に一つだけ生成されるBeanと、使うたびに新しく生成されるBeanの違いです。 スコープを理解すると、DIエラーの回避やパフォーマンス改善にもつながります。
2. Singletonスコープの基本と特徴
SingletonはMicronautで最も基本となるスコープです。 アプリケーション全体で一つだけBeanが生成され、複数のクラスから同じインスタンスが共有されます。 特に状態を持たないサービスクラスや、共通処理をまとめたクラスに向いています。
Micronautでは特別な指定をしなくても、BeanはデフォルトでSingletonとして扱われます。 そのため、初心者は意識せずにSingletonを使っているケースがほとんどです。
import jakarta.inject.Singleton;
@Singleton
public class GreetingService {
public String greet(String name) {
return "こんにちは、" + name;
}
}
上記のクラスはアプリケーション起動時に一度だけ生成され、DIによって使い回されます。 無駄なオブジェクト生成が発生しないため、パフォーマンス面でも優れています。
3. Prototypeスコープの仕組みと使いどころ
Prototypeスコープは、DIで注入されるたびに新しいインスタンスが生成されるスコープです。 毎回独立した状態を持たせたい場合に利用します。 リクエストごとに異なる値を保持するクラスなどに向いています。
Singletonと異なり、Prototypeは使い過ぎるとオブジェクト生成コストが増えるため注意が必要です。 役割を明確にした上で使い分けることが重要です。
import io.micronaut.context.annotation.Prototype;
@Prototype
public class RequestCounter {
private int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
このクラスはDIされるたびに新しいインスタンスが作られます。 他のクラスと状態を共有しないため、安全に使うことができます。
4. ThreadLocalスコープの考え方
ThreadLocalスコープは、スレッドごとにBeanのインスタンスを分けたい場合に利用します。 マルチスレッド環境で、スレッド単位の状態管理が必要な場面で活躍します。
Webアプリケーションでは、同時に複数のリクエストが処理されるため、 スレッド単位でデータを分離したいケースが存在します。
import io.micronaut.context.annotation.ThreadLocal;
@ThreadLocal
public class UserContext {
private String userId;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
}
ThreadLocalスコープは便利ですが、初心者のうちは無理に使う必要はありません。 まずはSingletonとPrototypeを正しく使い分けることが大切です。
5. スコープ選択を間違えたときのトラブル例
スコープを誤って選択すると、意図しないデータ共有や不具合の原因になります。 例えば、状態を持つクラスをSingletonにすると、複数処理でデータが上書きされる可能性があります。
逆に、すべてをPrototypeにすると、メモリ使用量が増加し、 パフォーマンス低下を招くこともあります。
MicronautのDI設計では、まずSingletonを基本とし、 明確な理由がある場合のみPrototypeやThreadLocalを選択する考え方が安全です。
6. 初心者向けスコープ使い分けの指針
初心者がMicronautで開発を進める場合、以下の考え方を意識すると理解しやすくなります。 共通処理やロジックはSingleton、状態を分離したい場合はPrototype、 スレッド単位の情報管理が必要なときだけThreadLocalを検討します。
スコープを意識して設計することで、DIエラーを減らし、 保守しやすいMicronautアプリケーションを構築できます。