スコープ
デフォルトでは、Guiceは常に新しいインスタンスを作成して返す。 このふるいまいはスコープによって変更可能である。 スコープによってインスタンスを再利用することができる。 つまり、そのライフタイムをアプリケーション(@Singleton)、セッション(@SessionScoped)、リクエスト(@RequestScoped)と区別することができる。
Guiceはウェブアプリ用のスコープを定義するためのサーブレット拡張を提供している。 それ以外のアプリケーションでは、カスタムスコープを作成することができる。
スコープの適用
Guiceはスコープを識別するためにアノテーションを使う。 実装クラスにスコープアノテーションを適用することにより、そのタイプのスコープを指定することができる。 機能的にもそうだが、ドキュメント用ととしてもこれらのアノテーションは役に立つ。 例えば、@Singletonはこのクラスがスレッドセーフであるべきことを示す(訳注:そうとは限らないと思うけど)。
@Singleton public class InMemoryTransactionLog implements TransactionLog { /* ここにあるものはすべてスレッドセーフであること */ }
スコープはbindにおいても指定することができる。
bind(TransactionLog.class).to(InMemoryTransactionLog.class).in(Singleton.class);
また、@Providesメソッドに指定することもできる。
@Provides @Singleton TransactionLog provideTransactionLog() { ... }
タイプ(のアノテーション)とbind()でスコープが異なる場合は、bind()の方が優先される。 アノテーションされたスコープをキャンセルしたい場合には、bind()時にScopes.NO_SCOPEを使うことができる。
リンクバインディングにおいては、スコープはバインディングソースの方に適用される。バインディングターゲットではない。 例えば、Bar,Grillインターフェースを実装するApplebeesというクラスがあるとする。 以下の二つのバインディングは二つのインスタンスを許す、一つはBar向けのもの、もう一つはGrill向けのものである。
bind(Bar.class).to(Applebees.class).in(Singleton.class); bind(Grill.class).to(Applebees.class).in(Singleton.class);
なぜかというと、スコープはバインドされるタイプ(Bar, Grill)に適用されるからであり、それらのタイプを満足するバインディング(Applebees)ではないからである。 もしただ一つのインスタンスだけを作成したいのであれば、そのクラスに対して@Singletonアノテーションを使うか、あるいは別のバインディングを使う必要がある。
bind(Applebees.class).in(Singleton.class);
このバインディングを使うと、他の二つのバインディングの.in(Singleton.class)句は不要になる。
in()句にはRequestScoped.classのようなアノテーションを入れることもできるし、ServletScopes.REQUESTといったインスタンスを入れることもできる。
bind(UserPreferences.class) .toProvider(UserPreferencesProvider.class) .in(ServletScopes.REQUEST);
アノテーションを使った方が望ましい。そのモジュールは別のタイプのアプリケーションでも再利用可能だからだ。 例えば、@RequestScopedオブジェクトであれば、ウェブアプリにおけるHTTPリクエストのスコープとして使うこともできるし、 APIサーバのRPCリクエストとして使うことができるからだ。
Eagerシングルトン
Guiceは早期に生成されるシングルトンのための特別なシンタックスを持つ。
bind(TransactionLog.class).to(InMemoryTransactionLog.class).asEagerSingleton();
Eagerシングルトンでは初期化エラーを早期に発見することができるし、エンドユーザは一貫性のあるきびきびした動きを得ることができる。 (これに対して)Lazyシングルトンでは、素早いedit-compile-run開発サイクルを提供する。 Stage enumにより、どちらのモードを選択するか決めることができる。
|
PRODUCTION |
DEVELOPMENT |
.asEagerSingleton() |
eager |
eager |
.in(Singleton.class) |
eager |
lazy |
.in(Scopes.SINGLETON) |
eager |
lazy |
@Singleton |
eager* |
lazy |
※Guiceはその知識のあるシングルトンについてのみ早期生成を行う。これらはモジュール内で指定されたものと、そこから依存されているものである。(訳注:@Singletonアノテーションはだめ?)
スコープの選択
オブジェクトがステートフルの場合、スコーピングをはっきりさせなければならない。 アプリに一つの場合は@Singleton、リクエストに一つの場合は@RequestScoped等など。 オブジェクトがステートレスで生成することが軽ければ、スコーピングは不要である。 バインディングをスコープ無しのままにしておけば、Guiceはそれが必要になった時にいつも新しいインスタンスを生成する。
シングルトンはJavaアプリではポピュラーな存在だがあまり多くの価値をもたない、特に依存性注入される場合には。 シングルトンはオブジェクト生成を省略する(後のゴミ集めも省略する)が、唯一のインスタンスへのハンドルを取得するには同期が必要である。 シングルトンは以下のようなケースで有用である。
- ステートフルなオブジェクト、コンフィギュレーションやカウンタなど
- 生成したりルックアップしたりするのが高価な(時間やメモリが必要な)オブジェクト
- データベースコネクションプールのようなリソースと結びついたオブジェクト
スコープと平行性
@Singleton、@SessionScoped等のアノテーションされたクラスはスレッドセーフである必要がある。 したがって、これらのに注入されるものもまたスレッドセーフである必要がある。
平行性からの保護の必要性のため、状態の量を制限するため変更可能性を最小限にする必要がある。
@RequestScopedオブジェクトはスレッドセーフである必要はない。 通常は、@Singleton,@SessionScopedが@RequestScopedに依存するのは間違いである。 より狭いスコープのオブジェクトに依存するようにすべきであり、そのオブジェクトのプロバイダを注入すべきである。
訳注:サーブレットでは一つのリクエスト・レスポンス間は一つのスレッドが用いられるため、@RequestScopedはシングルスレッドでの利用になる。