Revision 2 as of 2009-12-12 01:50:50

Clear message
Locked History Actions

guice/Manual/UserGuide/Scopes

スコープ

デフォルトでは、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アノテーションはだめ?)

スコープの選択

If the object is stateful, the scoping should be obvious. Per-application is @Singleton, per-request is @RequestScoped, etc. If the object is stateless and inexpensive to create, scoping is unnecessary. Leave the binding unscoped and Guice will create new instances as they're required.

Singletons are popular in Java applications but they don't provide much value, especially when dependency injection is involved. Although singletons save object creation (and later garbage collection), getting a handle to the single instance requires synchronization. Singletons are most useful for:

  • stateful objects, such as configuration or counters
  • objects that are expensive to construct or lookup
  • objects that tie up resources, such as a database connection pool.

スコープと平行性

Classes annotated @Singleton and @SessionScoped must be threadsafe. Everything that's injected into these classes must also be threadsafe. Minimize mutability to limit the amount of state that requires concurrency protection.

@RequestScoped objects do not need to be threadsafe. It is usually an error for a @Singleton or @SessionScoped object to depend on an @RequestScoped one. Should you require an object in a narrower scope, inject a Provider of that object.