如何使用ServletScopes.scopeRequest()和ServletScopes.continueRequest()?

时间:2011-12-06 15:04:07

标签: java guice

  1. 如何使用ServletScopes.scopeRequest()
  2. 如何在Callable中获取对@RequestScoped对象的引用?
  3. seedMap有什么意义?是否意味着覆盖默认绑定?
  4. 此方法与ServletScopes.continueRequest()之间的区别是什么?

1 个答案:

答案 0 :(得分:12)

回答我自己的问题:

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

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

    如果您需要将用户对象传递到Callable ,请继续阅读。

    假设您有一个将名称插入数据库的方法。我们有两种方法将名称传递给InsertName

    方法1 :使用子模块传递用户对象:

    1. 定义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(); } } }

      requestInjector.scopeRequest(InsertName.class, new AbstractModule()
      {
        @Override
        protected void configure()
        {
          bind(String.class).annotatedWith(Names.named("name")).toInstance("John");
        }
      })
      
    2. 绑定子模块中的所有用户对象,并使用RequestInjector.scopeRequest()来调用可调用范围:

      RequestInjector
    3. 我们在请求之外实例化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); } } (不需要提供者),因为它是在请求范围内注入的。

    4. Callable

      方法2 :在引用Provider<Foo>的请求之外注入call()。然后,get()方法可以seedMap请求范围内的实际值。对象对象通过InsertName传递(我个人觉得这种方法反直觉):

      1. 定义Callable,一个插入数据库的Providers。请注意,与方法1不同,我们必须使用@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(); } } }

        No implementation for String annotated with @com.google.inject.name.Named(value=name) was bound.
      2. 为您要传入的类型创建虚假绑定。如果不这样做,您将获得:ImmutableMap<Key<?>, Object> seedMap = ImmutableMap.<Key<?>, Object>of(Key.get(String.class, Names.named("name")), "john"); https://stackoverflow.com/a/9014552/14731解释为何需要这样做。

      3. 使用所需的值填充seedMap:

        ServletScopes.scopeRequest()
      4. 调用ServletScopes.scopeRequest(injector.getInstance(InsertName.class), seedMap);

        {{1}}