2011-12-06 4 views
7
  1. ServletScopes.scopeRequest()はどのように使用されますか?
  2. Callable内の@RequestScopedオブジェクトへの参照を取得するにはどうすればよいですか?
  3. seedMapのポイントは何ですか?それはデフォルトバインディングを上書きすることを意図していますか?
  4. この方法とServletScopes.continueRequest()の違いは何ですか?

答えて

12

は自分の質問に答える:

  1. ServletScopes.scopeRequest()新しいリクエストスコープで呼び出し可能実行されます。異なるスコープ間でオブジェクトを参照しないように注意してください。そうしないと、別の要求によって既に閉じられているデータベース接続を使用しようとするなど、スレッドの問題が発生します。 staticまたは最上位のクラスはあなたの友人です。
  2. に渡す前にCallableを注入してください。このため、あなたのCallableに含まれるフィールドに注意する必要があります。もっと詳しくはこちら。
  3. seedMapを入力すると、スコープのないスコープにオブジェクトを挿入できます。これは危険ですので、注射するものには注意してください。
  4. ServletScopes.continueRequest()は、の既存のリクエストスコープ内で実行される点を除いて同様です。現在のHTTPスコープのスナップショットを取得し、Callableでラップします。元のHTTPリクエストは完了します(サーバーからの応答が返されます)が、実際の操作は別のスレッドで非同期で完了します。 Callableが後で(別のスレッドで)呼び出されると、元のHttpServletRequestにアクセスできますが、HTTP応答やセッションにはアクセスできません。

これを行うにはどうすればよいですか?

あなたはCallableにユーザーオブジェクトを渡す必要がない場合:要求の範囲外Callableを注入し、ServletScopes.scopeRequest()にそれを渡します。 Callableは、Fooの代わりにProvider<Foo>を参照するだけです。そうしないと、要求スコープ外にインスタンスが挿入されてしまいます。

ユーザーオブジェクトをCallableに渡す必要がある場合は、をお読みください。

名前をデータベースに挿入するメソッドがあるとします。名前をCallableに渡すには、2通りの方法があります。

  1. InsertNameを定義し、データベースに挿入Callable

    @RequestScoped 
    private static class InsertName implements Callable<Boolean> 
    { 
        private final String name; 
        private final Connection connection; 
    
        @Inject 
        public InsertName(@Named("name") String name, Connection connection) 
        { 
        this.name = name; 
        this.connection = connection; 
        } 
    
        @Override 
        public Boolean call() 
        { 
        try 
        { 
         boolean nameAlreadyExists = ...; 
         if (!nameAlreadyExists) 
         { 
         // insert the name 
         return true; 
         } 
         return false; 
        } 
        finally 
        { 
         connection.close(); 
        } 
        } 
    } 
    
  2. 結合する全てのユーザオブジェクトで

    アプローチ1:子モジュールを使用して渡し、ユーザオブジェクト子モジュールとRequestInjectorを使用して呼び出し可能範囲をスコープします。scopeRequest():

    requestInjector.scopeRequest(InsertName.class, new AbstractModule() 
    { 
        @Override 
        protected void configure() 
        { 
        bind(String.class).annotatedWith(Names.named("name")).toInstance("John"); 
        } 
    }) 
    
  3. 我々は要求外RequestInjectorをインスタンス化し、それが、今度は、要求内側第Callableを注入します。 2番目のCallableは、リクエストスコープの内側に挿入されているため、Fooを直接参照できます(プロバイダーは不要です)。

import com.google.common.base.Preconditions; 
import com.google.inject.Inject; 
import com.google.inject.Injector; 
import com.google.inject.Key; 
import com.google.inject.Module; 
import com.google.inject.servlet.ServletScopes; 
import java.util.Collections; 
import java.util.Map; 
import java.util.concurrent.Callable; 

/** 
* Injects a Callable into a non-HTTP request scope. 
* <p/> 
* @author Gili Tzabari 
*/ 
public final class RequestInjector 
{ 
    private final Map<Key<?>, Object> seedMap = Collections.emptyMap(); 
    private final Injector injector; 

    /** 
    * Creates a new RequestInjector. 
    */ 
    @Inject 
    private RequestInjector(Injector injector) 
    { 
     this.injector = injector; 
    } 

    /** 
    * Scopes a Callable in a non-HTTP request scope. 
    * <p/> 
    * @param <V> the type of object returned by the Callable 
    * @param callable the class to inject and execute in the request scope 
    * @param modules additional modules to install into the request scope 
    * @return a wrapper that invokes delegate in the request scope 
    */ 
    public <V> Callable<V> scopeRequest(final Class<? extends Callable<V>> callable, 
     final Module... modules) 
    { 
     Preconditions.checkNotNull(callable, "callable may not be null"); 

     return ServletScopes.scopeRequest(new Callable<V>() 
     { 
      @Override 
      public V call() throws Exception 
      { 
       return injector.createChildInjector(modules).getInstance(callable).call(); 
      } 
     }, seedMap); 
    } 
} 

アプローチ2Provider<Foo>を参照要求外側Callableを注入。 call()メソッドは、get()の要求スコープ内の実際の値になります。オブジェクトオブジェクトは、(私は個人的にこのアプローチを反直感的に見つける)seedMapの仕方によって渡されます。

  1. は、データベースに挿入CallableInsertNameを定義します。アプローチ1とは異なり、我々はProvidersを使用しなければならないことに注意してください:。

    @RequestScoped 
    private static class InsertName implements Callable<Boolean> 
    { 
        private final Provider<String> name; 
        private final Provider<Connection> connection; 
    
        @Inject 
        public InsertName(@Named("name") Provider<String> name, Provider<Connection> connection) 
        { 
        this.name = name; 
        this.connection = connection; 
        } 
    
        @Override 
        public Boolean call() 
        { 
        try 
        { 
         boolean nameAlreadyExists = ...; 
         if (!nameAlreadyExists) 
         { 
         // insert the name 
         return true; 
         } 
         return false; 
        } 
        finally 
        { 
         connection.close(); 
        } 
        } 
    } 
    
  2. は、そうしないとあなたが得るあなたに渡したいタイプのための偽のバインディングを作成します。これが必要な理由No implementation for String annotated with @com.google.inject.name.Named(value=name) was bound.https://stackoverflow.com/a/9014552/14731は説明しています。

  3. 所望の値とseedMap移入:

    ImmutableMap<Key<?>, Object> seedMap = ImmutableMap.<Key<?>, Object>of(Key.get(String.class, Names.named("name")), "john"); 
    
  4. 起動ServletScopes.scopeRequest()

    ServletScopes.scopeRequest(injector.getInstance(InsertName.class), seedMap); 
    
関連する問題