异常表明ProcessHelper 是@RequestScoped。
当@Asynchronous 被调用时,会产生一个全新的独立线程,它不由 HTTP servlet 容器控制。因此,在该线程的上下文中,任何地方都没有 HTTP 请求或 HTTP 会话。你只能用@ApplicationScoped,不能用@RequestScoped,更不用说@SessionScoped。
至于ProcessManager 本身,@Stateless @ApplicationScoped 的组合没有意义。你很可能实际上想要一个@javax.ejb.Singleton。额外的好处是它是有状态的,因此您可以将流程结果作为实例变量保存在那里。
您提到ProcessHelper 反过来运行一些数据库查询。这意味着它应该在事务中运行。在这种情况下,您应该使它成为一个完全有价值的 EJB,而不是一个 CDI 托管 bean。因此,也将ProcessHelper 设为@Stateless,或者将所有数据库交互作业移至ProcessManager EJB。这也是可能的。
所以,总而言之,应该这样做:
<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;
}
}
请注意@Singleton 默认情况下是读/写锁定的。因此,在 start() 完成之前,您不能调用 getResult()。因此ConcurrencyManagementType.BEAN,这意味着它已解锁,因此本质上调用者本身负责并发管理。这使您可以在进程仍在运行时不断刷新页面。
另见: