如何解决在jsf表单提交中在后台运行任务
在我的JSF应用程序中,我有一个过程需要很长时间才能完成,我不希望用户一直等到完成为止。我正在尝试实现某种“解雇任务”以在后台运行。
我正在使用@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
或{ {1}}。
这是因为容器如何实现修改行为的功能。容器注入到EJB(和普通作用域的CDI Bean)客户端的对象实际上是 proxy ,它知道如何调用目标Bean的正确实例(例如e {{的正确实例)。 1}} bean)。代理还实现了额外的行为,例如@Inject
或@RequestScoped
。通过@Transactional
调用方法会绕过代理功能!因此,将这些批注放置在非公共方法上实际上是不可行的!
解决方案的非排他性列表:
-
将
@Asynchronous
移至另一个EJB,将其公开并保留this
批注 -
将
进行调用preProcess()
设为公开,然后从@Asynchronous
-
如果计算真正是
preProcess()
的私有对象,并且暴露出它会破坏设计,并且您不介意做更多的手动工作,则始终可以从容器提供的容器中运行异步任务Controller
:Service
请注意执行代码的线程的语义-更具体地说是传播哪些上下文值,什么不传播!好吧,您也必须注意
ManagedExecutorService
方法的问题!
技术原因确实是,它仅在您将其作为真实客户端调用时才起作用,而在内部调用时才起作用。
Nikos已经提出了两种方法,但是错过了一种方法:仅将EJB注入自身并调用它。
@Stateless
public class Service {
@Inject
private Service self;
public void save(Record record) {
// ...
self.preProcess(record);
}
@Asynchronous
public void preProcess(Record record) {
// ...
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。