Guice 在执行 CompletableFuture 时抛出 OutOfScopeException

Bar*_*ber 3 java guice completable-future

从请求范围的线程中,CompletableFuture必须由执行器中运行的任务来完成。所提供的供应商使用会话范围内的特定于域的服务MessageService。该服务是由 Guice 注入的。

public class MessageProcessingPage {
    private MessageService messageService;

    @Inject
    public MessagProcessingPage (MessageService messageService) {
        this.messageService = messageService;
    }

    // Called by request scoped thread.
    public void onProcessMessagesButton () {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        CompletableFuture.supplyAsync(
        // Called from a thread from the threadpool.
        () -> {return messageService.retrieveMessageMetadataSet(x, y);}
        , executorService);

        ...

    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

有一个被注入的MessageService(会话范围) 。MessageRestClient

@SessionScoped
public class MessageService {
    private MessageRestClient messageRestClient;

    @Inject
    public MessageRestClient (MessageRestClient messageRestClient) {
        this.messageRestClient = messageRestClient;
    }

    public MessageMetaDataSet retrieveMessageMetadataSet(x, y) {
        List<MessageMetaData> listOfMetaData = messageRestClient.retrieve(x, y, z);
        ...
    }

    ...
}

@SessionScoped
public class MessageRestClient {
    ...
}
Run Code Online (Sandbox Code Playgroud)

Guice 在尝试注入MessageRestClient.

java.util.concurrent.CompletionException: com.google.inject.ProvisionException: Unable to provision, see the following errors:

1) Error in custom provider, com.google.inject.OutOfScopeException: Cannot access scoped [MessageRestClient]. 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)

ServletScopes我在:中读到了有关方法的信息,public static <T> Callable<T> transferRequest(Callable<T> callable) 但我没有看到使用该方法的方法,因为没有 Callables 参与其中。你能帮我解决吗?

Rub*_*ben 5

当在 中处理 servlet 请求时Guice,它GuiceFilter会负责设置正确的上下文(通过 )ThreadLocal,以便它可以知道您处于哪个请求中,从而正确应用范围。注释的类的实例SessionScope实际上是代理,可以从 Guice 访问请求和会话信息并采取相应的操作。

您发送到的任务CompletableFuture在不受控制的单独线程中运行Guice。没有ThreadLocal可用的地方Guice可以获取该信息,因此,没有Request也没有Session信息,这意味着什么,没有[SessionScope。由于代理无法了解有关会话的任何信息,因此它会引发您收到的错误。

ServletScopes.transferRequest方法应注入所需的信息以便作用域发挥作用。应该像这样工作(但从未尝试过):

   Callable<MessageMetaDataSet> c = ServletScopes.transferRequest(
               () -> messageService.retrieveMessageMetadataSet(x, y));

   CompletableFuture.supplyAsync(
    () -> c.call()
    , executorService);
Run Code Online (Sandbox Code Playgroud)