2014-12-06 3 views
2

У меня есть следующая проблема с Guice: однопользовательская услуга, которой выдается поставщик контекстно-зависимой информации. До сих пор, контекст не был связан только с запросами сервлета, поэтому я использовал @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; 

} 

Это работает отлично. Теперь я работаю над добавлением обработки фоновой задачи в приложение. Фоновые задачи не инициируются из веб-запросов, поэтому я не могу использовать ServletScopes.scopeRequest (..). Я написал собственную область (почти точную копию BatchScoped из документа Giuce), чтобы каждая задача выполнялась в ее собственной области. Теперь возникает вопрос: как сделать 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, «Привязка к IContext был уже настроен на линии 1».

Вопрос в том, как правильно делать такую ​​инъекцию с помощью Guice?

+0

Я не понимаю, почему вы не можете использовать 'ServletScopes.scopeRequest()', это именно то, для чего он был предназначен. –

+0

Я не могу использовать его, потому что фоновая задача не инициируется из веб-запроса. Таким образом, RequestScope не доступен. – execc

+1

Вы попробовали? Я думаю, вы обнаружите, что это 'scopeRequest()' делает: он создает «поддельную» область запроса. –

ответ

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; 
+0

Спасибо за ответ. Не могли бы вы объяснить одно: мой сервис - одноэлементный, поэтому есть только один сайт для инъекций. Должен ли я каким-то образом ввести двух поставщиков и использовать ненулевой? Или спроектируйте своего рода делегата, ведь это делает (возможно, с прямым использованием инжектора?) – execc

+0

О, я неправильно понял.Я думаю, что предложения Тавиана звучат разумно для ваших требований, попробуйте. Если это не сработает, если предположить, что у вас есть другая переменная состояния, зависящая от области видимости, вы можете сделать свой переключатель поставщика на основе этого, введя его как параметр поставщика, но это звучит довольно уродливо. – The111

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. Проверьте Guav javadocs. Это те экземпляры, которые вы хотите уже присутствовать в области поддельного запроса. В зависимости от вашего IContext вам может понадобиться.

Смежные вопросы