备用豆(@ManagedBean)还是CDI豆(@Named)?

116

我刚开始阅读Core JavaServer Faces, 3rd Ed.,他们说了这个(重点在于我):

有两种不同的机制,CDI beans和JSF managed beans,可以用于JSF页面上,这是历史意外。 我们建议您使用CDI beans,除非您的应用程序必须在诸如Tomcat之类的普通servlet运行器上工作。

为什么?他们没有提供任何理由。在GlassFish 3上运行的原型应用程序中,我一直在使用@ManagedBean来处理所有的bean,并没有真正注意到任何问题。我并不介意从@ManagedBean迁移到@Named,但我想知道我为什么要费心


4
@ Bozho:那个问题非常相似,但是经过多次阅读Pascal的答案后,我仍然不理解为什么CDI要比其他技术优越。 我不了解CDI,但我很乐意学习,因为它“更好”。 它为什么更好? - Matt Ball
除非您的应用程序必须在纯servlet运行器(如Tomcat)上工作,否则我只使用Tomcat,并强烈推荐CDI。 Tomcat可以很好地支持它。 - Karl Kildén
1
@KarlKildén的“plain servlet runner”是指不支持CDI的Servlet容器。撰写本文时,Tomcat除非使用相当多的魔法,否则不支持CDI。 - Thorbjørn Ravn Andersen
5个回答

183

使用CDI。

根据JSF 2.3的规定,@ManagedBean已被弃用。请参见规范问题1417。这意味着没有理由再选择@ManagedBean而不是@Named。这在Mojarra 2.3.0 beta版本m06中首次实现。

enter image description here

自从Faces 4.0版本,根据规范issue 1547@ManagedBean已被移除

历史

核心区别在于,@ManagedBean 由JSF框架管理,并且只能通过 @ManagedProperty 提供给另一个JSF托管的bean。@Named 由应用服务器(容器)通过CDI框架管理,并且可以通过 @Inject 提供给任何类型的容器管理的构件,如 @WebListener@WebFilter@WebServlet@Path@Stateless 等,甚至是JSF中的 @ManagedBean。从另一方面来看,在 @Named 或任何其他容器管理的构件中,@ManagedProperty 不起作用。它实际上只在 @ManagedBean 中起作用。

另一个区别在于,CDI实际上会注入代理对象,以每个请求/线程为基础委派到目标范围中的当前实例(就像EJB的注入方式一样)。这种机制允许将较窄范围的bean注入到较宽范围的bean中,而使用JSF的@ManagedProperty则不可能实现。JSF在此处直接通过调用setter方法“注入”物理实例(这也正是为什么需要setter方法,而使用@Inject则不需要setter方法的原因)。
虽然不直接具有劣势——还有其他方法——但@ManagedBean的范围是有限的。从另一个角度来看,如果您不想为@Inject公开“太多”,您也可以将托管bean保持为@ManagedBean。这就像protectedpublic一样。但那并不重要。
至少在JSF 2.0/2.1中,通过CDI管理JSF backing bean的主要缺点是没有@ViewScoped的CDI等价物。 @ConversationScoped接近,但仍需要手动启动和停止,并且会向outcome URL附加一个丑陋的cid请求参数。 MyFaces CODI通过完全透明地桥接JSF的javax.faces.bean.ViewScoped到CDI,使其更容易实现,因此您只需执行@Named @ViewScoped即可,但是这会向outcome URL添加一个丑陋的windowId请求参数,即使是普通的页面导航也是如此。 OmniFaces通过真正将bean的范围绑定到JSF视图状态而不是任意请求参数来解决所有这些问题,提供了真正的CDI @ViewScoped

JSF 2.2(在此问题/答案发布3年后发布)提供了一个全新的完全CDI兼容的@ViewScoped注释,作为javax.faces.view.ViewScoped的一种变体。JSF 2.2甚至还带有仅限于CDI的@FlowScoped,它没有@ManagedBean等效项,从而推动JSF用户向CDI。预期@ManagedBean和其它相关内容将被Java EE 8弃用。因此,如果您目前仍在使用@ManagedBean,强烈建议切换到CDI以准备未来的升级路径。CDI已经在Java EE Web Profile兼容的容器中可用,例如WildFly、TomEE和GlassFish。对于Tomcat,您必须单独安装它,就像您已经为JSF做的那样。另请参见如何在Tomcat中安装CDI?


4
我已创建了 beans.xml,将 @ManagedBean 后备 bean 转换为 @Named,并将 @ManagedProperty 转换为 @Inject。这个世界一切正常。然而,如果我将我的 @EJB 注释更改为 @Inject,部署会失败(org.jboss.weld.exceptions.DeploymentException)并显示消息 WELD-001408 Injection point has unsatisfied dependencies。我是否应该使用 @Inject 将没有接口的 EJB 注入到 @Named bean 中,还是应该坚持使用 @EJB?这些 EJB 打包在一个 EJB JAR 中,在包含我的 CDI bean 的 WAR 中的同一 EAR 中。 - Matt Ball
它应该可以正常工作。您在当前的Weld版本中仍然遇到此问题吗? - BalusC
唉,我无法回答。这个问题是来自两个雇主和超过2年前的。根据我在Bozho的回答上的旧评论,我肯定已经转向使用CDI/@Named了。 - Matt Ball
MyFaces CODI通过完全透明地将JSF的javax.faces.bean.ViewScoped桥接到CDI,使其变得更加容易,因此您只需执行@Named @ViewScoped即可。但是,这会在结果URL上附加一个丑陋的windowId请求参数,甚至在纯香草页面导航中也是如此。请注意,使用DeltaSpike就不再适用了。如果您不需要Window Scope,则可以禁用dsId和windowId URL参数。 - JanM
1
@Jan:同时,OmniFaces也为JSF 2.0/2.1提供了类似于JSF 2.2的@ViewScoped:http://showcase.omnifaces.org/cdi/ViewScoped - BalusC

66

相对于纯粹的JSF,CDI更受欢迎,因为CDI允许在JavaEE中进行依赖注入。您还可以注入POJO并使它们被管理。使用JSF,您只能注入CDI所能注入的子集。


所以基本上,我可以使用CDI注入几乎任何类的实例(前提是它具有“正确的东西” - _这是什么,只是一个无参构造函数吗?_),而如果我想使用纯JSF注入它,则必须使用@ManagedBean - Matt Ball
3
马特,经过这些年,你能对这次迁移发表评论吗? - Koray Tugay
5
“@KorayTugay 我自从2011年6月以来就没有碰过这段代码,但我已经转换到了CDI,一切都很顺利。如果你有具体问题,我很乐意尽我所能地回答。” - Matt Ball

17

使用Java EE 6和CDI,您可以选择不同的托管Bean选项

  • @javax.faces.bean.ManagedBean 参考JSR 314,并在JSF 2.0中引入。其主要目标是避免在faces-config.xml文件中进行配置以在JSF页面中使用Bean。
  • @javax.annotation.ManagedBean(“myBean”)由JSR 316定义。它将JSF管理的bean泛化为可在Java EE中的其他地方使用。
  • @javax.inject.Named(“myBean”) 几乎与上述那个相同,除了您需要在web/WEB-INF文件夹中使用beans.xml文件来激活CDI。

1
第一和第二个有什么区别? - Matt Ball
第一个注解的目标是/曾经是用于替换faces-config.xml中的bean配置,以便在JSF中使用。第二个注解将该概念复制到“Java EE 6容器”中。它具有更多功能(如@PostConstruct和@PreDestroy注解),但也可以通过JSF页面(使用表达式语言)访问。 - h2mch
1
为什么需要一个 beans.xml 文件?这个说法今天是否仍然正确? - Thufir
2
不需要使用beans.xml了,使用JavaEE7即可。参见https://docs.oracle.com/javaee/7/tutorial/doc/cdi-adv001.htm。 - h2mch
1
使用JavaEE7,您不需要beans.xml文件: https://docs.oracle.com/javaee/7/tutorial/cdi-adv001.htm(正确的链接) https://blogs.oracle.com/theaquarium/entry/default_cdi_enablement_in_java(Java EE 7中的默认CDI启用) - M. Atif Riaz

2

我曾在GlassFish 3.0.1中使用CDI,但为了让它正常工作,我不得不导入Seam 3框架(Weld)。这样做效果还不错。

在GlassFish 3.1中,CDI停止工作,Seam Weld也随之停止运行。我在这里报告了这个问题,但至今未能解决。我不得不将所有的代码转换为使用javax.faces.*注解,但一旦CDI恢复正常,我计划返回CDI。

我同意你应该使用CDI,但有一个问题我还没有看到解决方案,那就是如何处理@ViewScoped注解。我的很多代码都依赖于它。如果有人能澄清这个问题,我将不胜感激。


-1
转化为中文:

迁移到CDI的一个好理由:您可以将一个共同的会话范围资源(例如用户资料)@Inject到JSF管理Bean和REST服务(即Jersey/JAX-RS)中。

另一方面,@ViewScoped是坚持使用JSF @ManagedBean的一个强有力的理由 - 特别是与重要的AJAX相关的任何内容。在CDI中没有标准替代方法。

似乎CDI Bean可能支持类似于@ViewScoped的注释,但我个人没有尝试过。

http://seamframework.org/Seam3/FacesModule


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