运行 jsf 表单提交的后台任务

Running a task in background on jsf form submit

在我的 JSF 应用程序中,我有一个过程需要很长时间才能完成,我不希望用户一直等到它完成。我正在尝试在后台实现某种 'fire and forget task' 到 运行。

我使用的是 @Asynchronous 方法。这是正确的做法吗?

我的控制器:

@ViewScoped
@Named
public class Controller implements Serializable {
    private static final long serialVersionUID = -6252722069169270081L;

    @Inject
    private Record record;

    @Inject
    private Service service;

    public void save() {
        this.record.generateHash();
        
        boolean alreadyExists = this.service.existsBy(this.record.getHash());
        
        if (alreadyExists)
            Messages.add(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "This record already exists"));
        else  {
            this.service.save(this.record);
            
            this.clearFields();
        }
    }
}

我的服务:

@Stateless
public class Service extends AbstractService<Record> {
    private static final long serialVersionUID = -6327726420832825798L;

    @Inject
    private BeanManager beanManager;

    @Override
    public void save(Record record) {
        super.save(record);
        
        this.preProcess(record);
    }

    @Asynchronous
    private void preProcess(Cd cd) {
        // Long task running here ...

        this.beanManager.fireEvent(cd);
    }
}

但即使使用这种方法,用户仍然停留在页面上,直到 preProcess 方法完成。

这里的问题是,修改 EJB(和 CDI bean)行为的注释仅在被注入到适当注入点的“代理”对象调用时应用,例如用 @EJB 注释的字段或 @Inject.

这是因为容器如何实现修改行为的功能。容器注入到 EJB 客户端(和 normal-scoped CDI bean)的对象实际上是一个 proxy,它知道如何调用目标 bean 的正确实例(例如正确的e @RequestScoped bean 的实例)。代理还实现了额外的行为,如 @Transactional@Asynchronous。通过 this 调用方法绕过代理功能!因此,将这些注释放在 non-public 方法上实际上是 NO-OP!

non-exclusive 个解决方案列表:

  • preProcess() 移动到不同的 EJB,使其成为 public 并保留 @Asynchronous 注释

  • 创建 preProcess() public 并从 Controller

    调用它
  • 如果计算真正是 Service 私有的并且公开它会破坏设计,并且您不介意做更多的手动工作,您总是可以 运行 来自 container-provided ManagedExecutorService:

    的异步任务
      @Resource
      private ManagedExecutorService managedExecutorService;
    

    注意执行代码的线程的语义 - 更具体地说,注意传播哪些上下文值,哪些不传播!那么,对于 @Asynchronous 方法,您也必须注意这一点!