在 DragonSlaying(屠龙)技术咨询小组的系列文章的第
5 部分,Allison Pearce Wilson 描述了 Go-ForIt.com 项目如何使用视图 bean。本文讨论的是视图 bean
如何解决一个公共设计难题 ― 在何处进行表示逻辑编码。Allison 说明了如何使用视图 bean 将 Web 应用中的组件清楚地分开,同时仍然提供丰富的用户界面。她还将带您一起研究展示
Go-ForIt.com 如何使用视图 bean 的代码示例。
我们的体系结构
与大多数标准电子商务应用一样,Go-ForIt 建立在逻辑上的 3 层体系结构之上,如下所述。
第一层
包含 Web 页面和用户的 Web 浏览器。
第二层
驻留在 WebSphere Application Server 上,拥有 servlet、JavaServer Page(JSP)和一些专门的
JavaBeans 组件。
第三层
包含用于处理事务和存储持久数据的商务对象、Enterprise JavaBeans(EJB)组件和 DB2 数据库。
这个 3 层的体系结构是模型视图控制器(MVC)设计的一个很好的示例。在 Go-ForIt 项目中,我们是 MVC 的狂热者。MVC 是一种描述应用设计的充满想象力的方法,它把应用设计分为多层,明确定义各个功能层,如下所示。
M(模型)层
您的后端数据或事务过程。在我们的应用中,EJB 组件充当模型。
V(视图)
最终用户看到的内容,比如 JSP 或 HTML 页面。
C(控制器)
我们的应用中的一个 servlet,它是担任“交通警察”角色的一层,协调用户与后端组件的交互作用。
遵循 MVC 设计原理的最主要的原因是确保不同的开发小组能够清楚地定义各自的职责,实现明确的分工。Web 开发者负责 JSP 和 HTML
页面的外观和感觉,而不涉及应用的业务逻辑。而 Java 开发者可以专注于创建应用的业务逻辑, 不管有关的 JSP 和 HTML 页面的外观和感觉。
通过使不同的开发者在各自的领域发挥出自己的长处,我们就不会有混乱的代码或非常丑陋的外观和感觉。
各层(layer 或 tier)之间一致的接口使得将组件配在一起的工作很简单, 并且几乎不需要了解不是您自己写的组件。
在下面的 MVC 示例中,一个用户从 HTML 页面提交了一个表单。一个 servlet 接收到了此请求并调用适当的后端组件,比如 EJB,此组件需实现此请求。此后端组件返回一个封装结果集的
JavaBeans 组件。然后 servlet 调用适当的 JSP 组件,该组件从结果 bean 检索结果。此 JSP 将动态结果数据与静态模板数据结合在一起并将页面返回给用户。每个层执行一个定义清楚的角色,使得小组成员之间可以很容易地分工。
视图控制器模型示例
JSP 中无 Java 代码!
MVC 听起来很好,对吗?实际上,它很难彻底分开各层,特别是涉及到视图时。视图经常(特别是当您处理表单数据时)要求一些逻辑来根据用户操作和某些条件显示正确的内容。
然而 MVC 要求视图组件,比如 JSP 与逻辑无关。在 Go-ForIt 项目中我们几乎是马上就遇到了这种左右为难的困境。
和许多应用一样,Go-ForIt 用户必须首先注册。我们的注册 JSP 页包含一个收集必要信息的表单。 此页面使用 JavaScript
进行一些客户端验证,然后提交表单以供处理。一个成功的注册会返回一条确认消息并指导用户转到适当的菜单页。
当表单处理发生错误时会怎样?我们假设一个用户试图用重复的用户标识进行注册。 当表单处理过程中发生这种问题时,我们希望发生以下几件事情:
让用户返回注册页面,并且页面顶部显示一条详细的出错消息。
用原来已经输入的数据预先填充表单,这样用户就不必重新输入每个数据项了。
HTML 表单域并非全部等同地被创建。在表单被提交并被返回以便修正错误后必需一个小逻辑来预先填充某些类型的域。在我们的例子中,我们有几个下拉列表框和复选框,它们需要一些逻辑来预先填充数据。起先,我们将“if...else”语句放入我们的
JSP 中,沿着一条不确定的路径开始。但是当我们的首席体系架构设计师(Lead Architect)无意中听到我们天真地谈论如何在 JSP
中编写所有的表示逻辑时,他说“JSP 中不准使用 Java 代码!”我们的常驻 MVC 斗士已经颁发了这条命令。
视图 bean 救了我们
记着这条命令,我们回到了制图板。我们决定:视图 bean 是解决“JSP 中不准使用 Java 代码!”问题的方案。视图 bean 是简单的
JavaBeans,它封装 JSP 中需要的表示逻辑。将 Java 代码放在 JSP 之外,将 Java 代码作为简单的外观和感觉模板。视图
bean 还允许我们把表示逻辑与我们的商业逻辑分离开来,将 HTML 代码放在 servlet 以及其它控件和模型对象之外。 它们是可重用的,易于使用,并使测试和调试更加容易。
重复,重复,再重复……
视图 bean 是 JavaBeans 实现,所以被设计得即简单又可重用。当我们开发自己的应用时, 我们可以在不同的 JSP 上使用相同的视图
bean。例如,我们使用的其中一个视图 bean 封装把错误报告回用户的表示逻辑。它包含检索正确的出错消息并以一种有意义的方式将其显示给用户的逻辑。
我们可以在任意页面调用这个 bean 并让它在我们希望的位置打印一条出错消息。
您可以在睡梦中做
任何熟悉 JavaBeans 规范的 Java 程序员都可能编写一个视图 bean,就象把一只手绑在背后也能赢得公司的足球比赛一样。视图 bean
对 Web 开发者来说也是难以置信地好用。他们可以简单地向需要调用视图 bean 的页面添加一个 JSP useBean 标记。简单的 JSP
getProperty 和 setProperty 标记将根据需要让 Web 开发者设置和检索 bean 的属性。
测试、调试,1-2-3 成了
在页面被请求之前 JSP 不会被编译。当 JSP 被请求时,应用服务器将 JSP 转换为 HTTPServlet,编译并执行它。当 JSP
代码没有错误时这个模型运行良好。但是,如果 JSP 有错误,JSP 执行模型会使测试和调试变得很困难,因为您每做一次修改,页面都不得不被重新转换为
servlet 并且被重新编译。您可以尝试调试 JSP 的生成的 servlet 代码,但这些代码非常让人费解而且对于人类读者不友好。另外,您还必须确定如何把生成的
servlet 代码中的问题转化为对 JSP 自身的修正。
然而,通过从 JSP 中除去 Java 代码,我们可以明显地减少 JSP 文件中错误出现的可能性。错误将更可能发生在我们从 JSP 调用的
Java 组件中,因为我们的所有逻辑都封装在这些组件中。我们可以利用强大的集成开发环境(Integrated Development Environment,IDE),它允许我们编译、测试和调试
Java 代码。在我们的项目中,我们用的是 IBM VisualAge for Java 企业版,版本 3.5。VisualAge 包含一个健壮的、集成的调试器和一个用于测试将要配置到
WebSphere Application Server 的应用的 WebSphere 测试环境(WebSphere Test Environment)。VisualAge
还递增地编译代码,所以您可以马上知道您的编译有没有错误。我们使用 VisualAge 开发、测试和调试我们的所有 Java 组件,包括我们的视图
bean。
添加视图 bean 之前和添加之后
当用户在 Go-ForIt 站点注册,且在服务器端处理期间捕获一个错误条件时,我们希望让用户返回到注册表单,且此表单顶端显示一条出错消息,他们输入的所有数据都已预先填入了表单。我们使用了视图
bean 来提供此功能,同时仍维持我们的 MVC 设计的完整性。我们来看一下涉及到的实际组件及有关代码。
下图显示了我们向体系结构添加视图 bean 之前和添加之后的应用流程。
视图 bean 添加之前的“用户注册”应用流程
视图 bean 添加之后的“用户注册”应用流程
问题的本质
如上面的“添加之后”图所示,在“用户注册”示例上我们使用了两个视图 bean:PrefillsRegistrationJSPViewBean
和 ErrorViewBean。每个 bean 封装一段特定的供 JSP 调用的表示逻辑。根据上面的图,您可能会认为视图 bean 增加了复杂性。然而,虽然它们确实向设计中添加了更多组件,但它们带来的封装却能够在维护、测试和重用时为我们节约时间。
PrefillsRegistrationJSPVi