108

我觉得 Java EE 6 规范有些混乱。有几组注释。

我们有像和用于创建 EJB的javax.ejb注解。@Stateful@Stateless

还有一个@javax.annotation.ManagedBean创建托管bean。

javax.enterprise.contextlike@SessionScoped和中有注解@RequestScoped

@ManagedBean更重要的是,包中还有@SessionScoped/@RequestScoped注释javax.faces.bean

为了让事情变得更复杂,有一个javax.inject带有@Named注释的包。

有人可以描述它们之间的关系吗?

我在哪里可以使用@EJB@Inject@ManagedPropery注入其他 bean?

4

3 回答 3

194

首先让我做一些澄清:

托管 bean 定义:通常托管 bean 是一个对象,其生命周期(构造、销毁等)由容器管理。

在 Java ee 中,我们有许多容器来管理其对象的生命周期,例如 JSF 容器、EJB 容器、CDI 容器、Servlet 容器等。

所有这些容器都是独立工作的,它们在应用程序服务器初始化中启动,并在部署时扫描所有工件的类,包括 jar、ejb-jar、war 和 ear 文件,并收集和存储一些关于它们的元数据,然后当你需要一个对象时在运行时,他们会给你这些类的实例,完成工作后,他们会销毁它们。

所以我们可以说我们有:

  • JSF 托管 bean
  • CDI 托管 bean
  • EJB 托管 bean
  • 甚至 Servlet 也是托管 bean,因为它们是由一个容器实例化和销毁的,该容器是一个 servlet 容器。

因此,当您看到 Managed Bean 一词时,您应该询问它的上下文或类型。(JSF、CDI、EJB 等)

然后你可能会问为什么我们有很多这样的容器:AFAIK,Java EE 的人想要一个依赖注入框架,但他们无法将所有需求收集到一个规范中,因为他们无法预测未来的需求,他们制作了 EJB 1.0,然后2.0,然后是 3.0,现在是 3.1,但 EJB 的目标只是满足一些需求(事务、分布式组件模型等)。

同时(同时)他们意识到他们也需要支持 JSF,然后他们制作了 JSF 托管 bean 和另一个 JSF bean 容器,他们认为它是一个成熟的 DI 容器,但它仍然不是完整和成熟的容器。

在那之后,Gavin King 和其他一些好人;) 制作了 CDI,这是我见过的最成熟的 DI 容器。CDI(受 Seam2、Guice 和 Spring 启发)是为了填补 JSF 和 EJB 之间的空白以及许多其他有用的东西,如 pojo 注入、生产者方法、拦截器、装饰器、集成 SPI、非常灵活等,它甚至可以做EJB 和 JSF 托管 bean 正在做什么,那么我们就可以拥有一个成熟且强大的 DI 容器。但是出于一些向后兼容性和政治原因,Java EE 的人想要保留它们!!!

在这里,您可以找到每种类型的区别和用例:

JSF 托管 Bean、CDI Bean 和 EJB

JSF 最初是使用自己的托管 bean 和依赖注入机制开发的,该机制在 JSF 2.0 中得到了增强,包括基于注释的 bean。当 CDI 与 Java EE 6 一起发布时,它被认为是该平台的托管 bean 框架,当然,EJB 已经过时了,它们已经存在了十多年。

问题当然是知道使用哪一个以及何时使用它们。

让我们从最简单的 JSF 托管 bean 开始。

JSF 托管 Bean

简而言之,如果您正在为 Java EE 6 开发并使用 CDI,请不要使用它们。它们为依赖注入和为网页定义支持 bean 提供了一种简单的机制,但它们远不如 CDI bean 强大。

它们可以使用@javax.faces.bean.ManagedBean带有可选名称参数的注释来定义。此名称可用于从 JSF 页面引用 bean。

范围可以使用javax.faces.bean包中定义的不同范围之一应用于 bean,包括请求、会话、应用程序、视图和自定义范围。

@ManagedBean(name="someBean")
@RequestScoped
public class SomeBean {
    ....
    ....
}

如果没有某种手动编码,JSF bean 不能与其他类型的 bean 混合。

CDI 豆

CDI 是作为 Java EE 6 的一部分发布的 bean 管理和依赖注入框架,它包括一个完整、全面的托管 bean 工具。CDI bean 比简单的 JSF 托管 bean 更加先进和灵活。他们可以使用拦截器、会话范围、事件、类型安全注入、装饰器、原型和生产者方法。

要部署 CDI bean,您必须将名为 beans.xml 的文件放在类路径上的 META-INF 文件夹中。一旦执行此操作,包中的每个 bean 都将成为 CDI bean。CDI 中有很多特性,这里就不一一赘述了,但作为 JSF 类特性的快速参考,您可以使用javax.enterprise.context包中定义的范围之一定义 CDI bean 的范围(即请求、会话,会话和应用范围)。如果要使用 JSF 页面中的 CDI bean,可以使用javax.inject.Named注释为其命名。要将一个 bean 注入到另一个 bean 中,请使用注解对字段进行javax.inject.Inject注解。

@Named("someBean")
@RequestScoped
public class SomeBean {

    @Inject
    private SomeService someService;
}

像上面定义的自动注入可以通过使用限定符来控制,它可以帮助匹配你想要注入的特定类。如果您有多种付款类型,您可以添加一个限定符来判断它是否是异步的。虽然您可以将@Named注释用作限定符,但您不应该使用它,因为它是为在 EL 中公开 bean 而提供的。

CDI 通过使用代理来处理具有不匹配范围的 bean 的注入。因此,您可以将请求范围的 bean 注入到会话范围的 bean 中,并且该引用对每个请求仍然有效,因为对于每个请求,代理都会重新连接到请求范围 bean 的实时实例。

CDI 还支持拦截器、事件、新的会话范围和许多其他特性,这使其成为比 JSF 托管 bean 更好的选择。

EJB

EJB 早于 CDI bean,在某些方面与 CDI bean 相似,而在其他方面则非常不同。首先,CDI bean 和 EJB 之间的区别在于 EJB 是:

  • 事务性的
  • 远程或本地
  • 能够钝化有状态的 bean 以释放资源
  • 能够使用定时器
  • 可以是异步的

这两种类型的 EJB 被称为无状态和有状态。无状态 EJB 可以被认为是线程安全的一次性 bean,它不维护两个 Web 请求之间的任何状态。有状态的 EJB 确实保持状态,并且可以在需要时被创建和保留,直到它们被处理掉。

定义 EJB 很简单,您只需在类中添加一个javax.ejb.Statelessjavax.ejb.Stateful注释。

@Stateless
public class BookingService {

  public String makeReservation(Item Item, Customer customer) {
    ...
    ...
  }
}

无状态 bean 必须具有依赖范围,而有状态会话 bean 可以具有任何范围。默认情况下它们是事务性的,但您可以使用事务属性注释。

虽然 EJB 和 CDI bean 在特性方面非常不同,但编写代码来集成它们非常相似,因为 CDI bean 可以注入到 EJB 中,而 EJB 可以注入到 CDI bean 中。将一种注入另一种时无需区分。同样,CDI 通过使用代理来处理不同的范围。一个例外是 CDI 不支持远程 EJB 的注入,但可以通过为其编写一个简单的生产者方法来实现。

javax.inject.Named注释以及任何限定符都可以在 EJB 上使用,以将其与注入点匹配。

什么时候使用哪个bean

你怎么知道什么时候使用哪个bean?简单的。

除非您在 servlet 容器中工作并且不想尝试让 CDI 在 Tomcat 中工作,否则永远不要使用 JSF 托管 bean(尽管有一些 Maven 原型,所以没有任何借口)。

通常,您应该使用 CDI bean,除非您需要 EJB 中可用的高级功能,例如事务功能。您可以编写自己的拦截器来使 CDI bean 事务化,但是现在,使用 EJB 更简单,直到 CDI 获得即将到来的事务性 CDI bean。如果您被困在 servlet 容器中并使用 CDI,那么手写事务或您自己的事务拦截器是没有 EJB 的唯一选择。

如果你需要@ViewScoped在 CDI 中使用,你应该

  • 使用seam-facesMyFaces CODI模块。只需将其中一个添加到您的类路径中即可@ViewScoped在 CDI 中工作。MyFaces CODI 对@ViewScoped 的支持更加稳固
  • 使用 MyFaces CODI's @ViewAccessScoped,它是 Apache 在 CDI 之上编写的扩展,只需下载它并使用@ViewAccessScoped注释而不是@ViewScoped.
  • 使用 CDI@ConversationScoped并使其长期运行。请参阅此处了解更多信息
  • 使用Omnifaces @ViewScoped 注解

有些零件是从这里偷来的。

于 2012-08-17T20:20:24.907 回答
7

是的,这可能会令人困惑。

由于某些ehm历史原因,JSF 和 CDI 对范围使用相同的注释,但来自不同的包。

正如您可能猜到的那样,这些来自javax.faces.beanJSF 规范,与 CDI 无关。除非您有充分的理由这样做,否则不要使用它们。并且永远不要将它们与来自javax.ejb. 这将产生无穷无尽的错误和细微异常列表。

我通常建议您浏览出色的Weld 文档的前几页(甚至更多页)。这应该会让您走上 Java EE 6 的轨道。

并随时在此处发布更多问题。

于 2012-08-16T13:43:14.013 回答
5

由于没有专门针对 的回复@javax.annotation.ManagedBean,这里有一个类似问题的答案的链接:Backing beans (@ManagedBean) or CDI Beans (@Named)? . 该规范可在http://download.oracle.com/otndocs/jcp/managed_beans-1.0-fr-eval-oth-JSpec/找到。所以在我看来,@javax.annotation.ManagedBean这应该是对@javax.faces.bean.ManagedBean.

从我收集到的信息来看,JSF Managed Beans 正在逐步淘汰,取而代之的是 CDI Beans(可能从 JSF 2.3 中被弃用?),所以我想@javax.annotation.ManagedBean现在变得更加过时了。

于 2014-06-17T11:19:12.100 回答