我正在使用 Guice 的 RequestScoped 和 Provider 以便在用户请求期间获取某些类的实例。这目前工作正常。现在我想在后台线程中做一些工作,使用在请求期间创建的相同实例。
但是,当我调用 Provider.get() 时,guice 返回错误:
Error in custom provider, com.google.inject.OutOfScopeException: Cannot
access scoped object. Either we are not currently inside an HTTP Servlet
request, or you may have forgotten to apply
com.google.inject.servlet.GuiceFilter as a servlet
filter for this request.
afaik,这是因为 Guice 使用线程局部变量来跟踪当前请求实例,因此不可能从与处理请求的线程不同的线程调用 Provider.get()。
如何使用 Provider 在新线程中获取相同的实例?有可能实现这个编写自定义范围吗?
最佳答案
我最近解决了这个确切的问题。您可以做一些事情。首先,阅读 ServletScopes.continueRequest()
,它包装了一个可调用对象,因此它会像在当前请求中一样执行。但是,这不是一个完整的解决方案,因为它不会转发 @RequestScoped
对象,只会转发 HttpServletResponse
之类的基本内容。那是因为 @RequestScoped
对象不是线程安全的。你有一些选择:
@RequestScoped
层次结构可以仅通过 HTTP 响应计算,那么您就大功告成了!不过,您将在另一个线程中获得这些对象的新实例。 RequestScoped
对象,但需要注意的是,它们都将被急切地实例化。 @RequestScoped
对象无法处理被急切地实例化,因为它们只适用于某些请求。我使用我自己的范围 @ThreadSafeRequestScoped
扩展了以下解决方案,并且只转发了这些解决方案。 代码示例:
public class RequestScopePropagator {
private final Map<Key<?>, Provider<?>> requestScopedValues = new HashMap<>();
@Inject
RequestScopePropagator(Injector injector) {
for (Map.Entry<Key<?>, Binding<?>> entry : injector.getAllBindings().entrySet()) {
Key<?> key = entry.getKey();
Binding<?> binding = entry.getValue();
// This is like Scopes.isSingleton() but we don't have to follow linked bindings
if (binding.acceptScopingVisitor(IS_REQUEST_SCOPED)) {
requestScopedValues.put(key, binding.getProvider());
}
}
}
private final BindingScopingVisitor<Boolean> IS_REQUEST_SCOPED = new BindingScopingVisitor<Boolean>() {
@Override
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
return scopeAnnotation == RequestScoped.class;
}
@Override
public Boolean visitScope(Scope scope) {
return scope == ServletScopes.REQUEST;
}
@Override
public Boolean visitNoScoping() {
return false;
}
@Override
public Boolean visitEagerSingleton() {
return false;
}
};
public <T> Callable<T> continueRequest(Callable<T> callable) {
Map<Key<?>, Object> seedMap = new HashMap<>();
for (Map.Entry<Key<?>, Provider<?>> entry : requestScopedValues.entrySet()) {
// This instantiates objects eagerly
seedMap.put(entry.getKey(), entry.getValue().get());
}
return ServletScopes.continueRequest(callable, seedMap);
}
}
关于multithreading - 多线程中的 Guice 和 RequestScoped 行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20505649/