JSF后端Bean结构(最佳实践)

121

我希望在这篇文章中,能够得到人们在JSF页面和后端bean之间最佳实践的意见。

有一件事情我总是无法解决,那就是后端bean的结构。此外,在这方面我从未找到过一篇好的文章。

哪些属性适合放在哪个后端bean中?在什么情况下添加更多属性到一个给定的bean中是合适的,而不是创建一个新的bean并将属性添加到其中?对于简单的应用程序,是否考虑只为整个页面拥有一个单独的后端bean,考虑到将一个bean注入另一个bean所涉及的复杂性?后端bean是否应包含任何实际业务逻辑,还是严格包含数据?

请随意回答这些问题以及可能出现的任何其他问题。


至于如何减少JSF页面和后端bean之间的耦合,我从不允许JSF页面访问任何后端bean属性的属性。例如,我从不允许这样的操作:

<h:outputText value="#{myBean.anObject.anObjectProperty}" />

我总是需要类似这样的东西:

<h:outputText value="#{myBean.theObjectProperty}" />

具有后端Bean值:

public String getTheObjectProperty()
{
    return anObject.getAnObjectProperty();
}

当我循环遍历一个集合时,我使用一个包装类来避免深入到数据表中的对象中。通常情况下,这种方法对我来说感觉“正确”。它避免了视图和数据之间的任何耦合。如果我错了,请纠正我。


你能举个例子吗:当我循环遍历一个集合时,我使用一个包装类来避免深入到数据表中的对象中。 - Koray Tugay
2
更多信息请参见BalusC在https://dev59.com/r2w05IYBdhLWcg3weBru#7223910的答案。 - Zack Marrapese
6个回答

147
你可能想要查看这个链接:区分不同类型的JSF管理bean
以下是Neil Griffin在上述文章中定义的不同bean类型的描述:
  • 模型管理Bean通常是会话范围。这种类型的管理Bean参与MVC设计模式中的“模型”关注点。当您看到“模型”一词时,请考虑数据。JSF模型Bean应该是一个遵循JavaBean设计模式的POJO,具有封装属性的getter/setter。模型Bean最常见的用例是作为数据库实体,或者仅表示数据库查询结果集中的一组行。
  • 后备管理Bean通常是请求范围。这种类型的管理Bean参与MVC设计模式中的“视图”关注点。后备Bean的目的是支持UI逻辑,并且与JSF视图或Facelet组合中的JSF表单具有1:1的关系。虽然它通常具有JavaBean样式的属性和相关的getter/setter,但这些是视图的属性--而不是底层应用程序数据模型的属性。JSF后备Bean也可以具有JSF actionListener和valueChangeListener方法。
  • 控制器管理Bean通常是请求范围。这种类型的管理Bean参与MVC设计模式中的“控制器”关注点。控制器Bean的目的是执行某种业务逻辑并向JSF导航处理程序返回导航结果。JSF控制器Bean通常具有JSF action方法(而不是actionListener方法)。
  • 支持管理Bean通常是会话或应用程序范围。这种类型的Bean在MVC设计模式中“视图”关注点中“支持”一个或多个视图。典型用例是为出现在多个JSF视图中的JSF h:selectOneMenu下拉列表提供ArrayList。如果下拉列表中的数据特定于用户,则将Bean保留在会话范围内。但是,如果数据适用于所有用户(例如省份的下拉列表),则将Bean保留在应用程序范围内,以便可以为所有用户缓存它。
  • 实用管理Bean通常是应用程序范围。这种类型的Bean为一个或多个JSF视图提供某种“实用”功能。这可能是可重复使用于多个Web应用程序的FileUpload Bean的很好示例。

8
这是一篇很棒的文章。我之前从未看过它,但肯定很高兴你发布了它。不管谁投了反对票都有点疯狂。这并不是针对 iceFaces 的特定内容。 - Zack Marrapese
3
实际文章的链接似乎已经失效。 - Bill Rosmus
10
然而,我仍然无法理解为什么这个回答目前获得了71个赞。按照这些规则实现他们的JSF应用程序的人无疑之后会抱怨JSF是一个非常不透明的框架,他们的JSF应用程序成为一团糟,并且他们都将责怪JSF本身而不是他们的错误方法和所谓的“最佳实践”。 - BalusC
这些 bean 中的任何逻辑是否在浏览器中执行,而不是在服务器中执行? - eskalera
@BalusC,您能解释一下JSF的正确方法应该是什么吗? - Ghos3t
显示剩余2条评论

14

很好的问题。当我转移到JSF时,我也遇到了同样的困境。这实际上取决于您的应用程序。我来自Java EE世界,因此建议在后端bean中尽可能少地包含业务逻辑。如果逻辑纯粹与页面呈现有关,则可以将其放在后端bean中。

我认为JSF的(许多)优点之一实际上是您可以直接在托管的bean上公开域对象。因此,我强烈建议采用<:outputText value="#{myBean.anObject.anObjectProperty}" />方法,否则您最终会在手动公开每个属性方面增加很多工作量。此外,在插入或更新数据时,如果封装所有属性,它还会变得有些混乱。在某些情况下,单个域对象可能不足够。在这些情况下,我会在公开对象之前准备一个ValueObject

编辑:实际上,如果您要封装要公开的每个对象属性,我建议您将UI组件绑定到后端bean,然后直接将内容注入组件的值中。

在bean结构方面,对我而言,转折点是当我强制忽略了所有我知道的关于构建Web应用程序的知识,并开始把它当作GUI应用程序来处理时。JSF很大程度上模仿Swing,因此开发Swing应用程序的最佳实践也大多适用于构建JSF应用程序。


感谢您的见解。我在Swing应用程序方面并没有做过太多(除了很久以前的学术项目)。有哪些关于Swing应用程序的好原则呢?此外,为什么在插入和更新值时会变得混乱?对我来说似乎都是一样的? - Zack Marrapese

5
我认为,对于你的后备bean来说,最重要的事情是分离它们的逻辑。如果你有一个CMS系统的前端页面,我会认为把每一段代码都放在一个bean中是不好的做法,因为:
  1. bean 最终会变得非常庞大
  2. 如果其他人在解决登录页面问题时可以轻松查找 loginBean.java 文件,那么他们就更容易找到他们想要的东西。
  3. 有时你会有一些明显不同于其余代码的小功能块,通过将其分离,当你已经拥有一个良好结构的bean时,我想你会发现重新开发/扩展这个代码变得更容易了。
  4. 如果/当你必须像这样声明 MyBigBean bigBean = new MyBigBean(); 而不是使用 LoginBean loginBean = new LoginBean(); 以使用你实际需要的功能,那么一个大型的bean 将使内存更加依赖(请纠正我如果我错了?)
  5. 在我看来,分离你的bean 就像分离你的方法。你不希望有一个运行超过100行的大方法,而是用处理其特定任务的新方法将其分割。
  6. 记住,除了你之外的其他人很可能也会在你的JSF项目上工作。


至于耦合,我不认为允许你的JSF页面访问后备bean中对象的属性是一个麻烦的问题。这是JSF内置的支持,实际上只是使读取和构建更容易。你已经严格分离了MVC逻辑。通过这样做,你可以节省大量的getter和setter代码行数。例如,我从Web服务中获得了一个非常巨大的对象,在我的演示中我需要使用一些属性。如果我为每个属性都创建一个getter/setter,那么我的bean将扩展至少100行变量和方法以获取属性。通过使用内置的JSF功能,我的时间和宝贵的代码行得到了节省。

这只是我的2美分,关于这个问题已经被标记为已回答。


1
然而,如果您的bean中有一个巨大的对象,而且您有15个EL函数从JSF页面深入挖掘该对象,那么您不仅要与bean打交道,还要与该对象打交道。因此,如果要删除该对象,则很难避免破坏UI。 - Zack Marrapese
1
但是你的后备 bean 也会与那个对象绑定吗?你的 UI 也会与后备 bean 绑定吗?当你需要修改它时,你将不得不在 UI 和 bean 中同时更改所有的 getters/setters。 - Chris Dale

4
我可能不能回答你的每一个问题,因为很多情况都是具体问题具体分析。
在你的后端bean中包含业务逻辑是可以的,这取决于你的实际需求。如果你正在实践领域驱动设计,你会倾向于将业务逻辑或持久化逻辑放入后端bean中。他们认为对象不应该只携带状态,还应该携带行为。另一方面,如果你考虑传统的Java EE方式,你可能会觉得在后端bean中存储数据,并将其他业务和持久化逻辑放在某个会话bean中更好。这也没问题。
整个页面只有一个后端bean也完全没问题。单独这个问题看起来可能不太对,但这要看具体情况而定。
你的另一个问题更依赖于你手头的具体情况。我倾向于使用领域驱动方法,在现有的bean中添加属性或创建新的bean可能都合适。哪个更好就用哪个。我认为这没有银弹。
哪些属性属于哪个后端bean。这不是取决于领域对象吗?或者说问题不是很清楚。
此外,在你给出的代码示例中,我并没有看到什么巨大的好处。

如果我们从使用使用JDBC查询创建的自制POJOs转换为具有略有不同字段名称的Hibernate实体,那么我们不仅需要更改支持bean,还需要更改JSF页面。但是我的代码示例不需要这样做,只需更改bean即可。 - Zack Marrapese
在这种情况下,您可以将支持bean实体化。然后,您只需要更改JSF页面。或者这取决于为什么要更改属性名称?只有当您将字段重命名以匹配数据库列名称时,才有意义。但那是另一种情况。 - Adeel Ansari

4

我不一定只保留每个页面一个后备bean。这取决于功能,但大多数情况下,我每个页面都有一个bean,因为大多数情况下一个页面处理一个功能。例如,在一个页面上,我有一个注册链接(我将与RegisterBean链接),以及一个购物篮链接(ShoopingBasketBean)。

我使用这个 <:outputText value="#{myBean.anObject.anObjectProperty}" /> ,因为我通常将后备bean作为操作bean来保存数据对象。我不想在我的后备bean中编写包装器来访问我的数据对象的属性。


0

我喜欢在没有视图的情况下测试业务代码,因此我将BackingBeans视为从视图到模型代码的接口。我从不在BackingBean中放置任何规则或过程。该代码进入Services或Helpers,以便重用。

如果您使用验证器,请将其放在BackingBean之外,并从验证方法中引用它们。

如果您访问DAO以填充Selects、Radios、Checkboxes,则始终在BackingBean之外进行。

相信我!您可以将JavaBean注入BackingBean中,但请尝试将BackingBean注入另一个BackingBean中。您很快就会陷入维护和理解代码的噩梦中。


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