CDI和EJB有何区别?它们如何互动?

111

我很难理解它们是如何互动的以及它们之间的边界在哪里。 它们是否重叠? 它们之间是否存在冗余?

我知道它们都有关联的注释,但我还没有找到具有简要描述的完整清单。 不确定这是否有助于澄清它们之间的区别或重叠处。

我只是感到困惑。 我(认为我)相当了解EJB,我想我很难理解CDI究竟带来了什么,以及它是如何取代或增强EJB已经提供的功能的。


5
这个问题在谷歌搜索“EJB CDI区别”中排名靠前,但我在https://dev59.com/4GYr5IYBdhLWcg3wq7-I上找到了更清晰的答案。 - matt freake
3个回答

199

目前的确有点令人困惑,因为Java EE中现在有多个组件模型。它们是CDIEJB3JSF托管bean

CDI是新贵。CDI bean具有依赖注入作用域事件总线。CDI bean在注入和作用域方面最灵活。事件总线非常轻量级,非常适合即使是最简单的Web应用程序。此外,CDI还提供了一种名为可移植扩展的高级功能,它是一种插件机制,供厂商向Java EE提供额外功能,并可在所有实现(Glassfish、JBoss AS、Websphere等)上使用。

EJB3 bean是从旧的遗留EJB2组件模型进行改装的,并且是Java EE中第一个通过注解管理的bean。EJB3 bean具有依赖注入声明式事务声明式安全性池化并发控制异步执行远程调用

与CDI bean相比,EJB3 bean的依赖注入不太灵活,并且EJB3 bean没有作用域的概念。但是,EJB3 bean默认情况下是事务性和池化的**,这是CDI选择留在EJB3领域中的两个非常有用的功能。其他提到的项目也不在CDI中可用。虽然EJB3没有自己的事件总线,但它确实有一种特殊类型的bean用于监听消息;这就是消息驱动bean。这可以用于从Java消息系统或任何其他具有JCA资源适配器的系统接收消息。对于简单的事件使用完整的消息传递比CDI事件总线更重量级,而EJB3仅定义了一个监听器,而没有生产者API。

JSF Managed Beans(JSF管理的Bean)是自从JSF被包含在Java EE中以来就一直存在的。它们也具有依赖注入作用域的功能。JSF Managed Beans引入了声明性作用域的概念。最初,这些作用域相当有限,在同一个Java EE版本中,EJB3 Bean已经可以通过注释声明,但JSF Managed Beans仍然必须在XML中声明。当前版本的JSF Managed Beans也终于可以通过注释声明,并且可以创建自定义作用域和视图作用域。视图作用域可以在相同页面的请求之间记住数据,这是JSF Managed Beans的一个独特功能。

除了视图作用域外,在Java EE 6中JSF Managed Beans几乎没有什么优势。CDI中缺少视图作用域是不幸的,因为否则CDI会成为JSF Managed Beans所提供的完美超集。更新:在Java EE 7 / JSF 2.2中添加了CDI兼容的@ViewScoped,使得CDI确实成为完美超集。更新2:在JSF2.3中,JSF managed beans已被弃用,改为使用CDI managed beans。

对于EJB3和CDI,情况并不那么清晰。EJB3组件模型和API提供了许多CDI没有提供的服务,因此通常情况下无法用CDI替换EJB3。另一方面,CDI可以与EJB3结合使用,例如为EJB添加作用域支持。

专家小组成员和实现了名为CanDI的CDI实现的Reza Rahman经常暗示,与EJB3组件模型相关的服务可以作为一组CDI注解进行适配。如果这样做,Java EE中的所有managed beans都可以变成CDI beans。这并不意味着EJB3会消失或变得过时,而只是它的功能将通过CDI而不是通过EJB自己的注释(如@Stateless和@EJB)公开。

更新

TomEE 和 OpenEJB 的大卫·布莱文斯在他的博客上很好地解释了 CDI 和 EJB 之间的差异和相似之处:CDI,何时使用 EJBs

* 虽然只是一个版本号的增量,EJB3 bean 在很大程度上是一种完全不同类型的 bean:一个简单的 POJO 通过应用一个简单的单一注释成为“托管 Bean”,而不像 EJB2 中的模型,其中每个 Bean 都需要一个笨重且过于冗长的 XML 部署描述符,除了 Bean 必须实现各种极其笨重且大多数毫无意义的组件接口。

** 无状态会话 Bean 通常是被缓存管理的,有状态会话 Bean 通常不是(但它们可以)。对于这两种类型,缓存是可选的,EJB 规范也没有规定要求哪种方式。


3
我对你所说的"EJB3 Bean没有作用域概念"和"EJB3没有自己的事件总线"有点困惑。这如何与David Blevin声称的"EJB就是CDI Bean,因此具有CDI的所有好处"相符?在你编写答案时和David编写博客文章时,在这方面有什么改变吗? - Chris
5
这是因为可能有些令人困惑的概念,实际上并没有所谓的“CDI bean”,而是将服务应用于托管的bean。为了讨论,人们(包括我自己)仍然称之为“CDI bean”。在CDI出现之前,EJB bean没有明确的作用域。正如David所解释的那样,Stateful隐含任何作用域(因此没有特定的作用域)。现在有了CDI,EJB bean可以利用CDI提供的作用域。如果没有CDI规范,仅查看EJB规范时,并没有明确的作用域。 - Arjan Tijms
1
你能详细说明一下“有应用于托管bean的服务”是什么意思吗?这是否意味着CDI bean实际上并不存在?它只是在POJO、EJB或JSF托管bean上提供额外功能的一些东西吗?比如在JSF托管bean中使用@Inject注解? - Koray Tugay
3
从 EJB 规范的角度进一步澄清,我们在 CDI 开始时就有意决定要求 EJB 实现必须完全支持 CDI 特性集上的 EJB。除了作用域必须限制为 Stateful beans 之外,CDI 的每个方面都适用于 EJBs。 - David Blevins
1
请注意,JSF 2.2 现在提供了 javax.faces.view.ViewScoped,这是一个 CDI 扩展,基本上是将 JSF 视图范围移植到 CDI。有了它,CDI 可以完全替代 JSF 管理的 Bean。 - jdessey
显示剩余2条评论

51

CDI: 依赖注入相关。它意味着您可以在任何地方注入接口实现。这个对象可以是任何东西,与EJB无关。这里有一个使用CDI注入随机数生成器的示例(链接),其中没有提到EJB。当您想要注入非EJB服务、不同的实现或算法时,可以使用CDI(因此根本不需要EJB)。
EJB: 您可能已经理解了,但很可能会被@EJB注释所困扰——它允许您将实现注入到您的服务或其他内容中。主要思想是注入类应该由EJB容器管理。 看起来CDI确实了解EJB,因此在符合Java EE 6标准的服务器中,在您的servlet中您可以同时编写两者。

@EJB EJBService ejbService;

@Inject EJBService ejbService;

这可能会让你感到困惑,但这很可能是EJB和CDI之间的唯一桥梁。

当我们谈论CDI时,您可以将其他对象注入到由CDI管理的类中(它们只需要由CDI感知的框架创建)。

CDI还提供了什么......例如,您使用Struts 2作为MVC框架(仅作为示例),在这里受限制,即使使用EJB 3.1-您也不能在Struts操作中使用@EJB注释,因为它没有被容器管理。但是,当您添加Struts2-CDI插件时,您可以为相同的东西编写@Inject注释(因此不再需要JNDI查找)。这样可以增强EJB的功能,但正如我之前提到的,您使用CDI注入的内容 - 它是否与EJB相关并不重要,这就是它的优势。

PS. 更新示例链接


@EJB和@Inject真的是功能上等价的吗?我认为是CDI和Java EE首字母缩写中的一些其他部分之间注入方法的重叠使我感到困惑。更多的阅读似乎表明,有希望对齐这些注释。 - Tim
@Maxym 当您使用 @Inject 时,如何确保 @Stateless 或任何其他 EJB 服务器端组件仍然使用容器提供的池化或并发等功能。我希望这不是由 CDI 提供的,对吗? - Bala
1
@Bala:CDI不提供连接池...请查看使用或不使用EJB3.1的CDI,希望能回答你的问题。 - Maxym
@KorayTugay:CDI是Java EE的一个特性,因此任何符合Java EE 6标准的服务器都有它(如果没有错的话,Glassfish 3.0.1+,JBoss 6+等)。您可以查看JBoss Weld,这是一个参考CDI实现,您可以在例如Tomcat中使用它... - Maxym

-1

阿尔伯特·爱因斯坦:如果你不能简单地解释它,那么你对它的理解还不够深刻

Ejbs和CDI非常容易理解。

Ejbs:

  1. 始终会被范围限定符注释,例如@Stateless、@Stateful、@Request等
  2. Ejbs的实例由Java EE框架控制和池化。EE框架有责任为消费者提供实例。

@Stateless

 public class CarMaker(){
    public void createCar(Specification specs){
        Car car = new Car(specs);
    }
}

CarMaker被注释为特定的Ejbs范围,因此它是Ejb。

CDI:

  1. 不完全由EE框架管理,实例必须由您自己创建。
  2. 它始终是依赖的。让我用一个例子来解释“依赖”:

    class Specification { private String color; private String model; //- Getter and Setter }

Specification类是CDI,因为它没有用Ejb范围进行注释,而且必须由您的代码初始化,而不是EE框架。 这里需要注意的一点是,由于我们没有对Specification类进行注释,因此默认情况下会使用@Dependent注释进行注释。

@Dependent  <- By default added 
class Specification { ... }

进一步阅读:您需要更深入地研究Ejbs范围注释和CDI范围注释之间的区别,这将进一步澄清概念。


爱因斯坦也说过:“一切都应该尽可能简单,但不要过于简单。”在这里,您可以(应该)用“解释”代替“制造”。 - Kukeltje

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