簡體   English   中英

訪問CDI SessionScoped bean在Java 8並行流中不起作用

[英]Accessing CDI SessionScoped bean doesn't work in Java 8 parallel stream

我無法理解為什么這段代碼不起作用。 基本上我想在parallelStream()函數期間從CDI ViewScoped bean訪問CDI SessionScoped bean,我得到以下異常:

WELD-001303: No active contexts for scope type javax.enterprise.context.SessionScoped

這是在Wildfly 10.1中運行的。

ViewScoped bean:

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;

@ViewScoped
@Named
public class TestController implements Serializable {
    private static final long serialVersionUID = 1L;

    @Inject SessionController sessionController;

    public void works() {
        List<Function<String, String>> functions = new ArrayList<>();
        functions.add((String input) -> {
            return sessionController.getSomething();
        });
        functions.add((String input) -> {
            return sessionController.getSomethingElse();
        });
        functions.stream().forEach(f -> f.apply("input"));
    }

    public void doesNotWork() {
        List<Function<String, String>> functions = new ArrayList<>();
        functions.add((String input) -> {
            return sessionController.getSomething();
        });
        functions.add((String input) -> {
            return sessionController.getSomethingElse();
        });
        functions.parallelStream().forEach(f -> f.apply("input"));
    }
}

SessionScoped bean:

import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;

@Named
@SessionScoped
public class SessionController implements Serializable {
    private static final long serialVersionUID = 1L;

    public String getSomething() {
        return "something";
    }
    public String getSomethingElse() {
        return "else";
    }
}

XHTML:

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:p="http://primefaces.org/ui">

    <h:head />

    <h:body>
        <h:form>
            <p:commandButton value="Works" action="#{testController.works}" />
            <br />
            <p:commandButton value="Does Not Work" action="#{testController.doesNotWork}" />
        </h:form>
    </h:body>
</html>

堆棧跟蹤:

Caused by: org.jboss.weld.context.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.SessionScoped
    at org.jboss.weld.manager.BeanManagerImpl.getContext(BeanManagerImpl.java:689)
    at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:90)
    at org.jboss.weld.bean.ContextualInstanceStrategy$CachingContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:165)
    at org.jboss.weld.bean.ContextualInstance.getIfExists(ContextualInstance.java:63)
    at org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:83)
    at org.jboss.weld.bean.proxy.ProxyMethodHandler.getInstance(ProxyMethodHandler.java:125)
    at com.SessionController$Proxy$_$$_WeldClientProxy.getSomething(Unknown Source)
    at com.TestController.lambda$3(TestController.java:33)
    at com.TestController.lambda$5(TestController.java:38)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:291)
    at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

我們有理論認為會話可能是特定於線程的,但沒有硬性證明。

如果有一個解決辦法,也很好奇。 真正的代碼比這復雜得多,所以我們不能事先預加載SessionController結果而不會失去並行流的好處。

我認為你要碰到的是使用並行流意味着你將在多個線程中運行。 現在,這是CDI和上下文的一個問題,因為您需要上下文傳播 - 例如,您當前擁有的主線程(例如)會話上下文處於活動狀態,但是當您創建另一個線程時,它在那里不活動。

規范中的第6.3章更深入地描述了它,但是為了給你提供簡短的故事 - 默認情況下,向其他線程的上下文傳播不起作用 並且有充分的理由 - 它將是非常昂貴的(同步)並且您將需要解決許多非常奇怪的情況,例如具有一個線程無效會話,因此在其他線程對其進行操作時取消激活會話上下文。 還有更多這樣的情況。

此外,沒有內置的解決方法。 你可以做的是,實現你自己的范圍,或者增強現有的會話范圍,但我想這會非常復雜。

如果要在Java EE中並行化查詢,我建議您使用ManagedExecutorService。 並行流使用的fork連接池用於並行計算,而不是IO。

當談到CDI上下文傳播時,您可能會發現BoundSessionContext很有用,假設您使用WELD作為CDI實現。 更多信息,請訪問http://john-ament.blogspot.fi/2014/01/bridging-netty-resteasy-and-weld.html

您可以在沒有綁定會話上下文的情況下進行管理。 只需從會話上下文中收集所有必要的數據並將其傳遞給工作線程。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM