Guice and RequestScoped behavior in multiple threads

I use Guice RequestScoped and Provider to get instances of some classes during user request. This currently works great. Now I want to do some work in the background thread using the same instances created during the request. However, when I call Provider.get (), guice returns an error:

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, this is because Guice uses local thread variables to keep track of current request instances, so it is not possible to call Provider.get () from a thread other than the thread that processes the request.

How can I get the same instances inside new threads using Provider? Can this be written in user area?

+4
source share
3 answers

I recently solved this exact problem. There are a few things you can do. First, read ServletScopes.continueRequest()which wraps the caller so that it runs as if it were in the current request. However, this is not a complete solution, because it will not forward objects @RequestScoped, but only basic things, such as HttpServletResponse. This is because objects @RequestScopedshould not be thread safe. You have several options:

  • If your whole hierarchy @RequestScopedcan only be calculated from an HTTP response, you're done! However, you will get new instances of these objects in another thread.

  • , 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);
    }
}
+6

, -. jOOQ , HTTP.

, . , .

. : . , HTTP- , ( ). . , .

Guice : ServletScope.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() {

    }
}

, , , . , , .

0

All Articles