使用两个不同范围的提供程序

时间:2014-12-06 20:54:56

标签: java dependency-injection guice

我对Guice有以下问题:单例服务注入了上下文相关信息的提供者。到目前为止,上下文仅与servlet请求相关,因此我使用了@RequestScoped提供程序,我正在服务中注入此提供程序,如下所示:

@RequestScoped
public class ContextProvider<IContext> implements Provider<IContext> {
  @Override
  public IContext get() { ... } // returns context        
}

@Singleton
public class ServiceImpl implements IService {

  @Inject
  private Provider<IContext> contextProvider;

}

工作正常。现在,我正在为应用程序添加后台任务处理。后台任务不是从Web请求启动的,因此我无法使用ServletScopes.scopeRequest(..)。我编写了一个自定义范围(几乎完全来自Giuce doc的BatchScoped副本),以使每个任务在其自己的范围内运行。现在的问题是 - 如何制作BatchScoped ContextProvider并配置Guice使用它?

我通过绑定EDSL进行了此尝试:

line 1 : bind(IContext.class).toProvider(ContextProvider.class).in(RequestScoped.class);
line 2 : bind(IContext.class).toProvider(BatchContextProvider.class).in(BatchScoped.class);

但Guice在第2行告诉我'已经在第1行配置了对IContext的绑定'。

问题是:使用Guice进行此类注射的正确方法是什么?

2 个答案:

答案 0 :(得分:2)

类似的问题:Getting multiple guice singletons of the same type

一般来说,这里的问题是你想要将同一个类绑定到两个不同的提供者(和范围,但实际上除此之外)。只有在为每个注释使用唯一的绑定注释时才可以这样做:

bind(IContext.class)
    .annotatedWith(MyAnnotation1.class)
    .toProvider(ContextProvider.class)
    .in(RequestScoped.class);
bind(IContext.class)
    .annotatedWith(MyAnnotation2.class)
    .toProvider(BatchContextProvider.class)
    .in(BatchScoped.class);

更改注射部位以包含相关注释:

@Inject
@MyAnnotationX
private Provider<IContext> contextProvider;

答案 1 :(得分:0)

您需要一个请求,该请求以您的后台任务开头并保留所有请求。这就是ServletScopes.scopeRequest的作用。

public class MyBackgroundTask extends Thread {
    @Override
    public void run() {
        RequestScoper scope = ServletScopes.scopeRequest(Collections.emptyMap());
        try ( RequestScoper.CloseableScope ignored = scope.open() ) {
            doTask();
        }
    }

    private void doTask() {

    }
}

哦,不要忘记使用提供程序,以便延迟检索依赖项。例如,说明上一个示例,以便后台任务使用您的IContext

public class MyBackgroundTask extends Thread {
    private Provider<IContext> contextProvider;

    @Inject
    public MyBackgroundTask(Provider<IContext> contextProvider) {
        this.contextProvider = contextProvider;
    }

    @Override
    public void run() {
        RequestScoper scope = ServletScopes.scopeRequest(Collections.emptyMap());
        try ( RequestScoper.CloseableScope ignored = scope.open() ) {
            doTask();
        }
    }

    private void doTask() {

    }
}

如果您不使用提供程序,则在此示例中,将从创建后台任务的线程完成注入,该后台任务可能位于另一个范围内。

BONUS :您可能已经注意到作为参数发送到scopeRequest方法的空地图。检查Guice javadocs。这些是您希望已经存在于虚假请求范围中的实例。根据您的IContext,您可能需要它。