多线程中的Guice和RequestScoped行为

Cly*_*men 4 multithreading guice

我正在使用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.
Run Code Online (Sandbox Code Playgroud)

afaik,这是因为Guice使用线程局部变量来跟踪当前请求实例,因此无法从与处理请求的线程不同的线程调用Provider.get().

如何使用Provider在新线程中获取相同的实例?有可能实现这种写作自定义范围吗?

Tav*_*nes 6

我最近解决了这个确切的问题.你可以做一些事情.首先,阅读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);
    }
}
Run Code Online (Sandbox Code Playgroud)