Jsf 如何在@Asynchronous方法中正确使用CDI?

Jsf 如何在@Asynchronous方法中正确使用CDI?,jsf,asynchronous,ejb,cdi,Jsf,Asynchronous,Ejb,Cdi,我有一个JSF2应用程序(运行在JBossAS7.1之上),当用户单击页面中的按钮时,它必须启动一个长过程。这个想法是要有一个不阻塞的交互,这样用户就可以等待并查看结果,或者简单地关闭页面,稍后再回来查看进展情况或结果,如果这个过程已经结束的话 流程本身在以下(简化)类中进行编码: 这样的类被标记为@ApplicationScoped,因为所有运行的进程(对于所有用户)都由它保存。因此,当单击按钮时,支持bean设置一些参数并调用异步方法start() 在进程尝试使用processHelper之

我有一个JSF2应用程序(运行在JBossAS7.1之上),当用户单击页面中的按钮时,它必须启动一个长过程。这个想法是要有一个不阻塞的交互,这样用户就可以等待并查看结果,或者简单地关闭页面,稍后再回来查看进展情况或结果,如果这个过程已经结束的话

流程本身在以下(简化)类中进行编码:

这样的类被标记为
@ApplicationScoped
,因为所有运行的进程(对于所有用户)都由它保存。因此,当单击按钮时,支持bean设置一些参数并调用异步方法
start()

在进程尝试使用
processHelper
之前,一切都正常,它运行许多Hibernate查询以继续进程的持久性部分。调用
processHelper
的第一个方法时,我得到以下异常:

WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped
作为附加信息,这种方法内部的断点永远不会被命中


发生了什么以及如何修复它?

我已经找到了解决这个问题的方法,但没有找到确切的答案。但它可能对你有用

显然,CDI不会用异步方法传播它的作用域(实际上它不会将上下文传播到任何其他线程),因此当您尝试
@Inject
一个资源时,您会收到一个错误。我找不到有关此行为的适当文档,但我遇到了至少两个问题,其中一个问题与您的问题非常相似:


如果我们假设这实际上是CDI的当前行为,那么解决此问题的最佳方法是去掉
@Inject
并创建
ProcessHelper
的新实例,或者去掉
@Asynchronous
注释并使用
ManagedExecutorService
,就像我在上面链接的第一个问题中所建议的那样。

例外情况表明
ProcessHelper
@requestscope

调用
@Asynchronous
时,会生成一个全新的独立线程,该线程不受HTTP servlet容器控制。在该线程的上下文中,因此任何地方都没有HTTP请求或HTTP会话。您只能使用
@ApplicationScoped
,不能使用
@RequestScoped
,更不用说
@SessionScoped

至于
ProcessManager
本身,组合
@Stateless@ApplicationScoped
没有意义。你很可能真的想要一个。额外的好处是它是有状态的,因此您可以将流程结果作为实例变量保存在那里

您提到,
ProcessHelper
依次运行一些数据库查询。这意味着它应该在事务中运行。在这种情况下,您应该使它成为一个完整的EJB,而不是CDI管理的bean。因此,也可以将
ProcessHelper
设置为
@无状态
,或者将所有DB交互作业移动到
ProcessManager
EJB中。这也是可能的

因此,总而言之,这应该做到:

<h:form>
    <h:commandButton value="Start" action="#{processBacking.start}" />
</h:form>
<p>
    Result (manually refresh page to check): #{processBacking.result}
</p>

请注意,
@Singleton
默认情况下是读/写锁定的。因此,在
start()
完成之前,您不能调用
getResult()
。因此,
ConcurrencyManagementType.BEAN
,这意味着它被解锁,因此本质上调用方自己负责并发管理。这样,只要进程仍在运行,您就可以一直刷新页面

另见:
<h:form>
    <h:commandButton value="Start" action="#{processBacking.start}" />
</h:form>
<p>
    Result (manually refresh page to check): #{processBacking.result}
</p>
@Named
@RequestScoped
public class ProcessBacking {

    @Inject
    private ProcessManager processManager;

    public void start() {
        // ...
        processManager.start(parameters);
    }

    public ProcessResult getResult() {
        return processManager.getResult();
    }

    // ...
}
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class ProcessManager {

    private ProcessResult result;

    @Inject
    private ProcessHelper helper;

    @Asynchronous
    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public void start(ProcessParameters parameters) {
        ProcessResult result = runSomeLongRunningNonTransactionalProcess(parameters);
        this.result = helper.persist(result);
    }

    public ProcessResult getResult() {
        return result;
    }

}
@Stateless
public class ProcessHelper {

    @PersistenceContext
    private EntityManager entityManager;

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public ProcessResult persist(ProcessResult result) {
        entityManager.persist(result);
        return result;
    }

}