2011-12-06 8 views

Respuesta

12

responder a mi propia pregunta:

  1. ServletScopes.scopeRequest() dirige una rescatable en una solicitud alcance nueva. Tenga cuidado de no hacer referencia a los objetos en diferentes ámbitos, de lo contrario terminará con problemas de enhebrado como intentar usar una conexión de base de datos que ya ha sido cerrada por otra solicitud. static o las clases de nivel superior son tus amigos aquí.
  2. Usted inyecta el Callable antes de pasarlo al ServletScopes.scopeRequest(). Por este motivo, debe tener cuidado con los campos que contiene su Callable. Más sobre esto a continuación.
  3. seedMap le permite inyectar objetos sin alcance en el alcance. Esto es peligroso, así que ten cuidado con lo que inyectas.
  4. ServletScopes.continueRequest() es similar, excepto que se ejecuta dentro de un alcance de solicitud existente. Toma una instantánea del alcance de HTTP actual y lo envuelve en un Callable. La solicitud HTTP original se completa (devuelve alguna respuesta del servidor) pero luego completa la operación real de forma asincrónica en una secuencia separada. Cuando se invoca el Callable en algún momento posterior (en ese hilo separado), tendrá acceso al HttpServletRequest original, pero no a la respuesta o sesión HTTP.

Entonces, ¿cuál es la mejor manera de hacerlo?

Si no necesita pasar por el usuario-objetos en el Callable: Inyectar la Callable fuera del ámbito de la petición, y se pasa dentro ServletScopes.scopeRequest(). El Callable solo puede hacer referencia a Provider<Foo> en lugar de a Foo, de lo contrario, terminará con instancias inyectadas fuera del alcance de la solicitud.

Si necesita pasar objetos de usuario en el Callable, siga leyendo.

Digamos que tiene un método que inserta nombres en una base de datos. Hay dos maneras para que pasemos el nombre al Callable.

Enfoque 1: Pass user-objetos utilizando un módulo de niño:

  1. Definir InsertName, un Callable que se inserta en la base de datos:

    @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. prender a todos los usuarios-objetos en un módulo hijo y alcance el llamante utilizando RequestInjector.scopeRequest():

    requestInjector.scopeRequest(InsertName.class, new AbstractModule() 
    { 
        @Override 
        protected void configure() 
        { 
        bind(String.class).annotatedWith(Names.named("name")).toInstance("John"); 
        } 
    }) 
    
  3. Nos instanciar un RequestInjector fuera de la solicitud y que, a su vez, se inyecta una segunda Callabledentro la solicitud. El segundo Callable puede hacer referencia directamente al Foo (sin necesidad de proveedores) porque se inyecta dentro del alcance de la solicitud.

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

Enfoque 2: Inyectar una Callable fuera de la petición que hace referencia a Provider<Foo>. El método call() puede entonces get() los valores reales dentro del alcance de la solicitud. Los objetos de objetos se pasan en por medio de un seedMap (personalmente encuentro este enfoque contrario a la intuición):

  1. Definir InsertName, un Callable que se inserta en la base de datos. Nótese que a diferencia de enfoque 1, debemos usar 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. crean vinculaciones falsas de los tipos que desea pasar en Si no obtendrá:. No implementation for String annotated with @com.google.inject.name.Named(value=name) was bound.https://stackoverflow.com/a/9014552/14731 explica por qué esto es necesario.

  3. Rellenar la seedMap con los valores deseados:

    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); 
    
Cuestiones relacionadas