启动CDI对话并将@ConversationScoped bean注入无状态会话bean

时间:2017-11-27 19:22:01

标签: java cdi stateless-session-bean conversation-scope seam-conversation

已经提出了类似的问题,但并没有完全解决我想要做的事情。我们有一个较旧的基于Seam 2.x的应用程序,我们正在转换为CDI的批处理作业框架。作业框架使用Seam Contexts对象来发起对话。作业框架还会加载一个特定于作业的数据持有者(基本上是一个Map),然后可以通过Seam Contexts对象访问链中的任何服务,包括来自SLSB。其中一些服务可以更新Map,因此可以更改作业状态并从记录到记录中进行检测。

在CDI中,作业将@Inject一个CDI Conversation对象,并手动开始/结束对话。我们还将定义一个包含Map(ConversationScoped)的新MapBean bean。对我来说不清楚的是两件事:

首先,作业还需要@Inject MapBean,以便在调用Conversation.begin()方法之前可以加载特定于作业的数据。容器是否知道将此实例传递给调用链中的服务?

与此相关,根据这个问题Is it possible to @Inject a @RequestScoped bean into a @Stateless EJB?,应该可以将一个ConservationScoped bean注入SLSB,但这看起来有点神奇。如果SLSB由不同的进程(作业,UI调用等)使用,它是否为每个调用获得单独的实例?

编辑澄清和简化的课程结构:

MapBean需要是ConversationScoped对象,包含特定实例/作业运行的数据。

@ConversationScoped
public class MapBean implements Serializable {
    private Map<String, Object> data;
    // accessors
    public Object getData(String key) {
        return data.get(key);
    }
    public void setData(String key, Object value) {
        data.put(key, value);
    }

}

工作将是ConversationScoped

@ConversationScoped
public class BatchJob {
    @Inject private MapBean mapBean;
    @Inject private Conversation conversation;
    @Inject private JobProcessingBean jobProcessingBean;
    public void runJob() {
        try {
            conversation.begin();
            mapBean.setData("key", "value");  // is this MapBean instance now bound to the conversation?
            jobProcessingBean.doWork();
        } catch (Exception e) {
            // catch something
        } finally {
            conversation.end();
        }
    }
}

作业可能会调用SLSB,并且当前对话范围的MapBean实例需要可用:

@Stateless
public class JobProcessingBean {
    @Inject private MapBean mapBean;

    public void doWork() {
        // when this is called, is "mapBean" the current conversation instance?
        Object value = mapBean.getData("key");
    }
}

我们的工作和SLSB框架相当复杂,SLSB可以调用许多其他服务或本地实例化的业务逻辑类,并且每个服务都需要访问对话范围MapBean

1 个答案:

答案 0 :(得分:4)

  

首先,作业还需要@Inject MapBean,以便在调用Conversation.begin()方法之前可以加载特定于作业的数据。容器是否知道将此实例传递给调用链中的服务?

是的,由于MapBean@ConversationScoped,因此从conversation.begin()conversation.end()的持续时间与调用链相关联。您可以将@ConversationScoped(以及@RequestScoped@SessionScoped)视为ThreadLocal中的实例 - 虽然每个线程都存在一个实例,但每个实例都绑定到该实例线程。

  

与此相关,根据这个问题Is it possible to @Inject a @RequestScoped bean into a @Stateless EJB?,应该可以将一个@ConservationScoped bean注入SLSB,但这看起来有点神奇。如果SLSB由不同的进程(作业,UI调用等)使用,它是否为每个调用获得单独的实例?

如果您发现这种模式与我上面解释的模式相同,那么它并不像您想象的那么神奇。 SLSB确实获得了一个单独的实例,但不仅仅是任何实例,即属于调用SLSB的范围的实例。

除了您发布的链接外,另请参阅this answer

我测试了与您发布的内容类似的代码,并且按预期工作 - MapBean与整个调用中注入的相同。小心两件事:

  • BatchJob也是@ConversationScoped,但没有实现Serializable,这将不允许bean进行钝化。
  • data未初始化,因此您将在runJob()中获得NPE。