2011-12-06 7 views

risposta

12

Rispondendo alla mia domanda:

  1. ServletScopes.scopeRequest() gestisce una Callable in una richiesta ambito nuova. Fai attenzione a non fare riferimento a oggetti su ambiti diversi, altrimenti ti ritroverai con problemi di thread come provare a utilizzare una connessione al database che è già stata chiusa da un'altra richiesta. static o le classi di primo livello sono i tuoi amici qui.
  2. Si inietta lo Callable prima di passarlo a ServletScopes.scopeRequest(). Per questo motivo, devi stare attento a quali campi contiene il tuo Callable. Maggiori informazioni su questo sotto.
  3. seedMap consente di iniettare oggetti senza ambito nell'ambito. Questo è pericoloso, quindi fai attenzione a ciò che inietti.
  4. ServletScopes.continueRequest() è simile tranne che viene eseguito all'interno di uno scope di richiesta esistente. Prende un'istantanea dell'ambito HTTP corrente e lo avvolge in un Callable. La richiesta HTTP originale completa (si restituisce una risposta dal server), ma si completa l'operazione effettiva in modo asincrono in una thread separata. Quando il Callable viene richiamato in un secondo momento (in quel thread separato) avrà accesso al HttpServletRequest originale ma non alla risposta o sessione HTTP.

Allora, qual è il modo migliore per farlo?

Se non è necessario passare user-oggetti nel Callable: Iniettare il Callable di fuori della portata richiesta e passa in ServletScopes.scopeRequest(). Callable può fare riferimento solo a Provider<Foo> anziché a Foo, altrimenti si finisce con istanze iniettate al di fuori dell'ambito della richiesta.

Se è necessario passare oggetti utente nello Callable, continuare a leggere.

Supponiamo di avere un metodo che inserisce i nomi in un database. Ci sono due modi per noi di passare il nome nello Callable.

Approccio 1: Pass user-oggetti utilizzando un modulo bambino:

  1. Definire InsertName, un Callable che inserisce nel database:

    @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. Bind tutti gli utenti-oggetti in un modulo figlio e scope il callable usando RequestInjector.scopeRequest():

    requestInjector.scopeRequest(InsertName.class, new AbstractModule() 
    { 
        @Override 
        protected void configure() 
        { 
        bind(String.class).annotatedWith(Names.named("name")).toInstance("John"); 
        } 
    }) 
    
  3. istanziamo un RequestInjector fuori della richiesta e, a sua volta, inietta una seconda Callableall'interno richiesta. Il secondo Callable può fare riferimento direttamente allo Foo (non è necessario per i provider) poiché è inserito all'interno dell'ambito della richiesta.

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); 
    } 
} 

Approccio 2: Iniettare un Callable di fuori della domanda che fa riferimento Provider<Foo>. Il metodo call() può quindi get() i valori effettivi all'interno dell'ambito della richiesta. Gli oggetti oggetto sono passati in per mezzo di un seedMap (Personalmente trovo questo metodo intuitivo):

  1. Definire InsertName, un Callable che inserisce nel database. Si noti che a differenza di approccio 1, dobbiamo usare 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. creare associazioni fasulle per i tipi che si desidera passare Se non si otterrà:. No implementation for String annotated with @com.google.inject.name.Named(value=name) was bound.https://stackoverflow.com/a/9014552/14731 spiega perché questo è necessario.

  3. Popolare la seedMap con i valori desiderati:

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

    ServletScopes.scopeRequest(injector.getInstance(InsertName.class), seedMap); 
    
Problemi correlati