2011-12-06 62 views

回答

12

回答我的问题:

  1. ServletScopes.scopeRequest()运行在请求范围可赎回。注意不要在不同范围内引用对象,否则最终会遇到线程问题,例如尝试使用已被另一个请求关闭的数据库连接。 static或顶级课程是你的朋友。
  2. 您在将其传递到ServletScopes.scopeRequest()之前注入Callable。出于这个原因,你必须小心你的Callable包含什么字段。更多关于下面的内容。
  3. seedMap允许您将对象注入到作用域中的非作用域对象。这是危险的,所以要注意你注入的内容。
  4. ServletScopes.continueRequest()是类似的,只是它在现有的请求范围内运行。它获取当前HTTP范围的快照并将其包装在Callable中。原始HTTP请求完成(您从服务器返回一些响应),但是然后在单独的线程中异步完成实际操作。当稍后调用Callable时(在单独的线程中),它将有权访问原始的HttpServletRequest,但不能访问HTTP响应或会话。

那么,最好的办法是什么?

如果您不需要用户对象传递到Callable:注射请求范围之外Callable,并将其传递到ServletScopes.scopeRequest()Callable可能只引用Provider<Foo>而不是Foo,否则最终将在实例之外注入实例。

如果您需要将用户对象传入Callable,请继续阅读。假设你有一种将名字插入数据库的方法。有两种方法可以将名称传入Callable

方法1

  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. 绑定的所有用户对象传递使用子单元的用户对象一个子模块,并使用RequestInjector调用可调用范围。scopeRequest():

    requestInjector.scopeRequest(InsertName.class, new AbstractModule() 
    { 
        @Override 
        protected void configure() 
        { 
        bind(String.class).annotatedWith(Names.named("name")).toInstance("John"); 
        } 
    }) 
    
  3. 我们实例化一个RequestInjector请求外,它反过来,注入第二Callable内部该请求。第二个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); 
    } 
} 

方法2:注射引用Provider<Foo>请求之外的Callablecall()方法可以在get()请求范围内的实际值。对象对象由一个seedMap的方式通过了(我个人认为这种方法反直觉):

  1. 定义InsertName,一个Callable是插入到数据库中。注意,与方法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);