2012-01-23 18 views
2

Guiceの:Requestスコープに注釈付きの型を注入することはできません、私がリクエストスコープに注釈付きの変数を注入しようとしています

/** 
* A Callable that is constructed in one scope and injects a Callable into a potentially separate 
* scope. 
* <p/> 
* @param <V> the type of object returned by the Callable 
* @author Gili Tzabari 
*/ 
public final class InjectingCallable<V> implements Callable<V> 
{ 
    private final Injector injector; 
    private final Class<? extends Callable<V>> delegate; 

    /** 
    * Creates a new InjectingCallable. 
    * <p/> 
    * @param injector the Guice injector 
    * @param delegate the class to inject and delegate to 
    */ 
    public InjectingCallable(Injector injector, Class<? extends Callable<V>> delegate) 
    { 
     Preconditions.checkNotNull(injector, "injector may not be null"); 
     Preconditions.checkNotNull(delegate, "delegate may not be null"); 

     this.injector = injector; 
     this.delegate = delegate; 
    } 

    @Override 
    public V call() throws Exception 
    { 
     return injector.getInstance(delegate).call(); 
    } 
} 

GetModule:InjectingCallableはRequestスコープ内GetModuleを注入し、

Map<Key<?>, Object> seedMap = ImmutableMap.<Key<?>, Object>builder(). 
    put(Key.get(String.class, Names.named("name")), name).build(); 
return ServletScopes.scopeRequest(new InjectingCallable<>(injector, 
    GetModule.class), seedMap).call(); 

@RequestScoped 
private static class GetModule implements Callable<Module> 
{ 
    private final String name; 
    private final Session session; 

    @Inject 
    public GetModule(@Named("name") String name, Session session) 
    { 
     this.name = name; 
     this.session = session; 
    } 
} 

このコードを実行すると、このエラーが発生しますROR:私はそれが動作グローバルスコープに同じ変数をバインドする場合

1) No implementation for java.lang.String annotated with @com.google.inject.name.Named(value=name) was bound. 
    while locating java.lang.String annotated with @com.google.inject.name.Named(value=name) 

。注釈を削除すると、その注釈が機能します。この問題は、Requestスコープの注釈付き変数に固有のようです。何か案は?

+0

ImmutableMapの場合は、左辺にジェネリックスがあるので、イコール記号の右側の 'builder()'コールでジェネリックを指定する必要はありません。それはstatic builder()メソッドの全体的なポイントです。すなわち、 - ImmutableMap 、オブジェクト> seedMap = ImmutableMap.builder()。put(...)。build() ' – Tom

+1

@Tom、私はそれを行うと私はコンパイラのエラーを取得します。Java7 update 2では、デフォルトでに右側が解決されます。あなたの最後にそれをコンパイルし、同じことがわかったら教えてください。 – Gili

答えて

5

問題は、このタイプのバインディングがないことです。明示的に値を設定しているからといって、バインドする必要はありません。あなたが言うことができます:

bind(String.class) 
    .annotatedWith(Names.named("name")) 
    .toProvider(Providers.<String>of(null)); 

、その後name変数に値"foo"を持っている場合は、それを播種しているので、あなたが"foo"を注入されます。値をシードするとスコープ(これは単なるキャッシュ)に置かれ、Guiceは値のプロバイダを実行しません。 nullのプロバイダーを使用することによって、値が播種されていない場合、値を爆発させることができます。

要するに、Guiceは、スコープを手動でシードするかどうかにかかわらず、すべての依存関係をプロビジョニングする方法を指定する必要があります(これは非常にまれなことです)。

一部の迷惑なアドバイス: - インジェクタは注入しないでください。この種の問題をより捉えることが難しくなります。 1つの「ルートオブジェクト」を持つのが最善です。これは、injector.getInstanceを呼び出す必要がある単一のオブジェクトです。多くのアプリケーションでは、これはアプリケーションサーバーにすぎません。 (例えば、 - injector.getInstance(MyServer.class).startServer())。なぜこれがあなたを助けますか?起動時にすべての依存関係が満たされているかどうかを簡単に検出できます。要求中にインジェクタを注入し、それを呼び出して任意のオブジェクトを作成すると、実行時にバインディングが失われてプロビジョニングエラーが発生する危険性があります。また、あなたのgetInstance呼び出しを早期に行う場合は、Guiceバインディングが満たされているかどうかを簡単にテストできるように、これを行うテストを書く方が簡単です。

UPDATE:

If I bind the same variable to the global scope it works.

うーん、あなたは基本的に私がやったことをしましたか?もしそうなら、上記の私の説明はなぜ動作するのかを説明します:-)。 GuiceのはString以来Stringのための結合が:-)空のコンストラクタを持っていないので、この作品

If I remove the annotation, it works.

理由があります。基本的には、単一の@Inject-ableコンストラクタ、引数なしコンストラクタ、または型をバインドするプロバイダを用意する必要があります。

+0

Btw、私は注射器を注射するべきではないと言っているのではない...しかし、それは非常にめったに必要ではない。おそらくあなたが望むところにguiceベースのフレームワークを書いている状況でのみ使うべきであるそれらに物を注入する特別な方法を提供します。そして、それを実行しても、起動後に驚きがないように、適切な 'requireBinding'呼び出しを試みてください。 – Tom

+0

私は、代わりに(プライベートモジュールを持つ)子インジェクタを使用しました。私はこのアプローチが、決して使用されないプロバイダをバインドするよりもはっきりと分かっていました。明確化のためにありがとう! – Gili

+0

有用なIllegalStateExceptionをスローするカスタムプロバイダを使用することを検討してください(たとえば、「要求スコープで提供されていない@Named(\ "name \")Stringインスタンス」) - 間違えた場合、NullPointerExceptionよりも多くの助けが得られます –

関連する問題