Java EE 6 @ javax.annotation.ManagedBean与@ javax.inject.Named与@ javax.faces.ManagedBean


107

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

我们有和javax.ejb一样的注释@Stateful@Stateless用于创建EJB。

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

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

软件包中还有/ @ManagedBean和注解。@SessionScoped@RequestScopedjavax.faces.bean

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

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

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


Answers:


194

首先让我做一些澄清:

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

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

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

所以我们可以说我们有:

  • JSF管理的bean
  • CDI管理的豆
  • 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与Jav​​a EE 6一起发布时,它被视为该平台的托管bean框架,当然,EJB已经淘汰了十多年了。

当然,问题在于知道使用哪个以及何时使用它们。

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

JSF托管Bean

简而言之,如果您正在为Java EE 6开发并使用CDI,请不要使用它们。它们提供了一种简单的机制,用于进行依赖项注入和定义Web页面的后备bean,但是它们的功能远不及CDI bean。

可以使用@javax.faces.bean.ManagedBean带有可选name参数的注释来定义它们。该名称可用于引用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,必须在类路径的META-INF文件夹中放置一个名为beans.xml的文件。完成此操作后,程序包中的每个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?简单。

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

通常,除非需要EJB中可用的高级功能(例如事务功能),否则应使用CDI bean。您可以编写自己的拦截器以使CDI Bean具有事务性,但是就目前而言,使用EJB更为简单,直到CDI即将获得交易CDI Bean。如果您被困在servlet容器中并且正在使用CDI,那么没有EJB的唯一选择是手写事务或您自己的事务拦截器。

如果您需要@ViewScoped在CDI中使用,则应

  • 使用接缝面MyFaces CODI模块。只需将其中之一添加到您的类路径中即可@ViewScoped在CDI中使用。MyFaces CODI对@ViewScoped的支持更加可靠
  • 使用MyFaces CODI's @ViewAccessScoped,它是Apache在CDI之上编写的扩展,只需下载它并使用@ViewAccessScoped注释代替@ViewScoped
  • 使用CDI @ConversationScoped并使其长期运行。有关更多信息,请参见此处
  • 使用Omnifaces @ViewScoped批注

一些零件从这里偷走


3
这很棒!谢谢!为了完整起见,仅需说明如何将CDI或EJB bean注入JSF bean。是@ManagedProperty("#{someBean})"正确的方法吗?
Piotr Gwiazda 2012年

2
不!它不会工作。只需使用@Named和对其进行注释即可将jsf托管Bean转换为CDI托管Bean ,@javax.enterprise.context.RequestScoped然后使用@Inject注释使用CDI注入。如果不需要,请不要使用jsf托管bean。
Mehdi 2012年

3
> JEE家伙想保留它们!!!-比这更微妙。CDI在Java EE 6周期的后期才完成,并且JSF 2和JAX-RS都已经完成。他们的反应增强了。已经介绍了他们自己的托管bean设施。如果CDI较早可用,情况可能会有所不同。在Java EE 7中,JSF将采用CDI,并且最终将弃用javax.faces.bean(尽管弃用在Java EE中是一个缓慢的过程,但它有好有坏)。
Arjan Tijms'8

3
当您说:要部署CDI bean时,必须在类路径上的META-INF文件夹中放置一个名为beans.xml的文件。完成此操作后,程序包中的每个bean都会变成一个CDI bean。您是说每个bean除了原来的之外还都变成了CDI bean吗?如果我拥有带有ManagedBean和ViewScoped的JSF ManagedBeans,该怎么办。它们仍然是JSF托管Bean吗?
Koray Tugay 2013年

3
有人能够在这篇很棒的文章上对Java EE 7进行更新吗?
Martijn Burger 2014年

7

是的,这可能令人困惑。

对于一些EHM历史原因JSF和CDI使用的是相同的注解范围,但是从不同的包。

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

我通常建议您浏览优秀的Weld文档的前几页(甚至更多页)。这将使您步入Java EE 6的轨道。

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


实际上,我有两个问题:1.我经常发现视图范围非常有用。我需要使用JSF注释吗?2.这意味着@javax.annotation.ManagedBean没有用,因为CDI将所有类都视为托管bean,对吗?
Piotr Gwiazda 2012年

不完全的。您将需要使用例如Seam Faces将JSF范围桥接到CDI。是的,如果您在相关的jar文件中有bean.xml,则不需要@ManagedBeans。哦,如果您还有其他问题,最好先开始一个新线程,然后再在评论部分中放松自己。
jan groth 2012年

3

由于没有关于的专门答复@javax.annotation.ManagedBean,因此有指向类似问题答案的链接:是支持bean(@ManagedBean)还是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托管Bean正在逐步淘汰,而倾向于CDI Bean(也许已从JSF 2.3中弃用了?),所以我想@javax.annotation.ManagedBean现在它已越来越过时了。


@Named将来会取代@ManagedBean吗?
Thufir

1
我已经阅读了不同的Java EE专家的几条声明,他们预测CDI @Namedbean将取代JSF @ManagedBeans,例如在stackoverflow.com/questions/4347374/…中,BalusC表示:“期望@ManagedBean和朋友将根据Java被弃用。 EE 8”。
HeinBlöd2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.