Использование провайдера из двух разных областей
У меня есть следующая проблема с 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 doc), чтобы каждая задача выполнялась в своей собственной области. Теперь вопрос - как сделать 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?
2 ответа
Аналогичный вопрос: получение нескольких одинаковых одинаковых типов
В общем, проблема здесь в том, что вы хотите связать один и тот же класс с двумя разными провайдерами (и областями, но это на самом деле не относится к делу). Это возможно только в том случае, если вы используете уникальные аннотации привязки для каждого, например, так:
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;
Вам нужен поддельный запрос, который начинается с вашей фоновой задачи и остается для всего этого. Что это 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() {
}
}
Если вы не используете провайдеров, инъекция в этом примере будет выполняться из потока, который создает фоновую задачу, которая может находиться в другой области видимости.
БОНУС: Возможно, вы заметили пустую карту, отправленную в качестве параметра scopeRequest
метод. Проверьте Guice Javadocs. Это те экземпляры, которые вы хотите, чтобы они уже присутствовали в вашей области фальшивых запросов. В зависимости от вашего IContext
вам может понадобиться