java - 访问 CDI SessionScoped bean 在 Java 8 并行流中不起作用

标签 java jsf jsf-2 java-8 cdi

我无法理解为什么这段代码不起作用。基本上我想在 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)

我们有理论认为 session 可能是特定于线程的,但没有确凿的证据。

也很好奇是否有解决方法。实际代码比这复杂得多,因此我们无法在不失去并行流优势的情况下预先加载 SessionController 结果。

最佳答案

我认为您遇到的是使用并行流意味着您将在多个线程中运行。现在,这是 CDI 和上下文的问题,因为您需要上下文传播——例如在主线程中,您当前有(例如) session 上下文处于 Activity 状态,但是当您创建另一个线程时,它在那里不处于 Activity 状态。

Chapter 6.3 in spec更深入地描述了它,但给你一个简短的故事 - 默认情况下,上下文传播到其他线程不起作用。并且有充分的理由 - 这将是非常昂贵的(同步)并且您需要解决大量非常奇怪的情况,例如具有线程失效 session 之一,因此在其他线程对其进行操作时停用 session 上下文。还有更多这样的情况。

此外,没有针对此问题的内置解决方法。你可以做的是实现你自己的范围,或者增强现有的 session 范围,但我想这会非常复杂。

关于java - 访问 CDI SessionScoped bean 在 Java 8 并行流中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43265022/

相关文章:

java - 更改 Primefaces WIZARD 组件的 NEXT 按钮位置

tomcat - 如何使 Tomcat 8 Web 应用程序上的现有 JSF2 成为 OpenID Connect SSO 服务器的客户端

java - Primefaces - 未捕获的语法错误 : Unexpected token ILLEGAL

java - Java 中的 if (boolean)unreachable 语句

java - 如何将 build.xml 添加到 Eclipse 中的现有 GWT 项目?

java - 使用基于反射的静态调用而不是接口(interface)

java - 当我返回页面(Java server Faces)时,如何重新初始化下拉列表的选定值?

java - 闭包意味着完全类型安全的标准?

java - 如何将数据从 JSF 传递到 Java Applet

jsf - 在简单的 JSF 应用程序中显示空白字段的自定义错误消息