在哪些地方可以使用EJB 3.1和CDI?

136
我正在制作一个基于Java EE的产品,其中使用了GlassFish 3和EJB 3.1。
我的应用程序拥有session beans,调度程序并使用Web服务。我最近了解到Apache TomEE,它支持Contexts and Dependency Injection (CDI)。 GlassFish容器也支持CDI。
如果我不需要CDI没有提供的任何功能,我可以替换会话bean吗?那么我可以得到哪些好处?
3个回答

459
是的,你可以自由地混合使用CDI和EJB,并取得一些很好的结果。听起来你正在使用@WebService和@Schedule注释,这是将EJB加入到混合中的好理由。
有很多混淆之处,所以这里提供一些关于EJB和CDI相关信息的概述。

EJB >= CDI

请注意,EJB是CDI bean,因此具有CDI的所有优点。反之则不成立(尚未)。所以绝对不要习惯性地思考“EJB vs CDI”,因为这个逻辑实际上转化为“EJB + CDI vs CDI”,这是一个奇怪的方程式。
在Java EE的未来版本中,我们将继续对齐它们。对齐的意思是允许人们做他们已经能够做的事情,只是没有在顶部添加@Stateful、@Stateless或@Singleton注解。

在实现方面的EJB和CDI

最终,EJB和CDI共享相同的基本设计,即代理组件。当您获取对EJB或CDI bean的引用时,它不是真正的bean。相反,您获得的对象是一个伪造品(代理)。当您在该伪造对象上调用方法时,调用会转到容器,容器会通过拦截器、装饰器等进行调用,同时处理任何事务或安全检查。完成所有这些后,调用最终传递回代理并传递结果给调用者。
唯一的区别在于如何解析要调用的对象。通过“解析”,我们简单地指出容器在哪里以及如何查找真正的实例以进行调用。
在CDI中,容器会查找一个“范围”(scope),这基本上是一个哈希表,它在特定的时间段内存在(每个请求@RequestScoped、每个HTTP Session @SessionScoped、每个应用程序@ApplicationScoped、JSF Conversation @ConversationScoped或您自定义的作用域实现)。
在EJB中,如果bean类型为@Stateful,则容器也会查找哈希表。@Stateful bean还可以使用上面提到的任何范围注释,导致它与范围中的所有其他bean一起生存和消亡。在EJB中,@Stateful实际上是“任何范围”的bean。@Stateless基本上是一个实例池——您可以从池中获取一个实例,持续一个调用。@Singleton本质上是@ApplicationScoped。
因此,在基本层面上,你可以用“EJB” bean做的任何事情,你都应该能够用“CDI” bean做。除了实例的解析方式之外,所有管道都是相同的。

目前来说,容器所提供的代理服务并不完全相同,但是我们正在Java EE规范层面上进行改进。

性能注意事项

请忽略任何你可能会拥有的“轻量级”或“重量级”的印象。那都是市场宣传。就大部分而言,它们具有相同的内部设计。CDI实例解析可能更加复杂,因为它稍微更加动态和上下文相关。相比之下,EJB实例解析则相当静态、简单和愚笨。

从TomEE的实现角度来看,调用EJB与调用CDI bean几乎没有任何性能差异。

默认使用POJOs,然后使用CDI,再然后才是EJB

当没有好处时,请勿使用CDI或EJB。当你开始需要注入、事件、拦截器、装饰器、生命周期跟踪等内容时,请添加CDI。这大多数情况下都适用。

除了这些基础知识外,如果你想要使用一些有用的容器服务,你只有在将CDI bean添加为EJB(通过添加@Stateful@Stateless@Singleton)时才有这个选项。

以下是我使用EJB的短列表。

使用JAX-WS

暴露一个JAX-WS @WebService。我很懒。当@WebService也是EJB时,你不必将其列出并在web.xml文件中进行映射。这对我来说是一项工作。此外,我还可以使用下面提到的任何其他功能选项。所以这对我来说是一个明智的选择。

仅限于@Stateless@Singleton使用。

使用JAX-RS

通过@Path公开一个JAX-RS资源。我还是很懒。当RESTful服务也是EJB时,你再次获得自动发现,并且不必将其添加到JAX-RS的Application子类中或执行其他任何操作。此外,如果需要的话,我还可以将完全相同的bean公开为@WebService或使用下面提到的任何一种伟大的功能。

仅限于@Stateless@Singleton使用。

启动逻辑

通过@Startup实现启动时的加载。在CDI中目前没有与此相当的功能。容器生命周期中似乎漏掉了添加类似于AfterStartup事件的内容。如果我们这样做了,你只需要有一个监听该事件的@ApplicationScoped bean,它将有效地与@Singleton@Startup相同。这已列入CDI 1.1的待办事项列表。

仅适用于@Singleton

并行工作

@Asynchronous方法调用。在任何服务器端环境中,开启线程都是不可取的。太多线程会严重降低性能。该注释允许您使用容器的线程池并行执行任务。这很棒。

适用于@Stateful@Stateless@Singleton

计划工作

@ScheduleScheduleExpression基本上是cron或Quartz功能。同样非常棒。大多数容器在内部使用Quartz来实现这一点。然而,大多数人不知道,在Java EE中进行计划工作是事务性的!如果您更新数据库并计划一些工作,并且其中一个失败,则两者都将自动清除。如果EntityManager的持久化调用失败或刷新存在问题,则无需取消计划工作。太好了,有事务支持。

仅适用于@Stateless@Singleton

在JTA事务中使用EntityManagers

上述关于事务的说明当然需要您使用JTA管理的EntityManager。您可以在普通的“CDI”中使用它们,但是没有容器管理的事务,重复UserTransaction提交/回滚逻辑可能变得非常乏味。

适用于所有Java EE组件,包括CDI、JSF @ManagedBean@WebServlet@WebListener@WebFilter等。然而,@TransactionAttribute注释仅适用于@Stateful@Stateless@Singleton

保留由JTA管理的EntityManager

EXTENDED管理的EntityManager允许您在JTA事务之间保持EntityManager打开状态并不会丢失缓存数据。对于正确的时间和地点来说是很好的功能。请负责任地使用:)

仅适用于@Stateful

简单同步

当你需要同步时,@Lock(READ)@Lock(WRITE) 注解非常棒。它允许你免费获得并发访问管理,省去了所有的 ReentrantReadWriteLock 管理。在同一篮子里还有 @AccessTimeout,它允许你指定线程在放弃之前等待获取 bean 实例的时间。

仅适用于 @Singleton bean。


36
我的天啊David :) 我想你说得很好。 - LightGuard
8
谢谢您的回答。您已经清理了我头脑中的困惑,让我豁然开朗,许多事情也顿时联系到一起了。 - Thupten
9
这是我曾读过针对该主题最好的解释,它还详尽地涵盖了EJB在实际使用中的几乎所有重要方面。干得好! - nanoquack
3
非常易懂,Adam在法律术语上并没有错,但区别无关紧要。规范说明EJB实例不是上下文相关的,但随后又说对EJB的引用(代理)是上下文相关的。Stateful bean的生命周期完全由引用(代理)控制,因此当CDI容器控制该引用(代理)时,数学计算结果相同 - Stateful EJB实际上可以是上下文相关的。 - David Blevins
11
这个答案是从2012年的,我们有最近的更新吗? - abbas
显示剩余11条评论

2

如果你真的没有使用ejb 3.1的任何功能,那么答案很简单。但是,我猜你的问题表明你怀疑有一些你正在从中受益的ejb 3.1概念,而你并不知道它们。一个例子可能是容器可以保持一组slsb准备好被使用,这样jms和数据库连接就不必作为请求的一部分注入。


0
谢谢你提供这么详细的见解。 但是我常常在想:什么时候需要使用从池中获取的无状态会话Bean,而不是简单地使用现有的应用范围CDI Bean呢?毕竟后者也可以以无状态的方式被多个线程访问。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接