Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1532352
  • 博文数量: 3500
  • 博客积分: 6000
  • 博客等级: 准将
  • 技术积分: 43870
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-03 20:31
文章分类

全部博文(3500)

文章存档

2008年(3500)

我的朋友

分类:

2008-05-04 21:30:48

一起学习
视图 bean:将 Java 代码与 JSP 组件分离

Allison Pearce Wilson (allison1@us.ibm.com)
电子商务体系架构设计师,IBM
2001 年 8 月

在 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 将动态结果数据与静态模板数据结合在一起并将页面返回给用户。每个层执行一个定义清楚的角色,使得小组成员之间可以很容易地分工。

视图控制器模型示例
显示 MVC 各层元素的图表

JSP 中无 Java 代码!
MVC 听起来很好,对吗?实际上,它很难彻底分开各层,特别是涉及到视图时。视图经常(特别是当您处理表单数据时)要求一些逻辑来根据用户操作和某些条件显示正确的内容。 然而 MVC 要求视图组件,比如 JSP 与逻辑无关。在 Go-ForIt 项目中我们几乎是马上就遇到了这种左右为难的困境。

和许多应用一样,Go-ForIt 用户必须首先注册。我们的注册 JSP 页包含一个收集必要信息的表单。 此页面使用 JavaScript 进行一些客户端验证,然后提交表单以供处理。一个成功的注册会返回一条确认消息并指导用户转到适当的菜单页。

当表单处理发生错误时会怎样?我们假设一个用户试图用重复的用户标识进行注册。 当表单处理过程中发生这种问题时,我们希望发生以下几件事情:

  1. 让用户返回注册页面,并且页面顶部显示一条详细的出错消息。
  2. 用原来已经输入的数据预先填充表单,这样用户就不必重新输入每个数据项了。

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 增加了复杂性。然而,虽然它们确实向设计中添加了更多组件,但它们带来的封装却能够在维护、测试和重用时为我们节约时间。

PrefillsRegistrationJSPView 的职责
PrefillsRegistrationJSPView bean(快点!说三遍)封装一个捕获(set 方法)用户早已输入某些表单域的数据,并在用户被引回到页面时将这些值预先填入表单(get 方法)的逻辑。 使用这种设计,遇到错误(比如重复的用户标识)的用户将回到表单,且只须更改用户标识,而不必重新输入整个表单。 下面的代码摘自 PrefillsRegistrationJSPView.java。这段代码并不显示全部类定义, 但显示域声明及一个复选框属性(顾客)和一个列表框属性(称谓)的 getter 和 setter 方法。需要相当多的逻辑来预先填充一个小列表框和一个复选框。

PrefillsRegistrationJSPView.java

\** This is an excerpt from PrefillsRegistrationJSPView **\

 

public class PrefillRegistrationJSPView {

   private String _prefillTypeCustomer = null;

   private java.lang.String _prefillTitle = null;

   private java.lang.String[] _titles = {

      "", "Mr.", "Mrs.", "Ms.", "Miss"

      };

 

   . . . 

 

};

 

public String getCustomerChecked() {

 

   if (_prefillTypeCustomer != null) 

      return "checked";

   else        

      return null;

}

 

public java.lang.String getTitleSelect() {

   StringBuffer list = new StringBuffer();

   list.append("");

      return list.toString();

}

 

public void setCustomerChecked(String val) {

        _prefillTypeCustomer = val;

}

 

public void setInitialTitle(java.lang.String prefillTitle) {

        _prefillTitle = prefillTitle;

}

}

ErrorViewBean 的职责
ErrorViewBean 封装显示出错消息的逻辑,这些出错消息源于服务器端处理发现的错误。例如,当用户输入一个重复的用户标识时,User EJB 会抛出一个 DuplicateUserException 异常。如果抛出了一个 DuplicateUserException 异常,RegistrationServlet 则将捕获此异常并用来自 DuplicateUserException 的出错消息将一个 ErrorView bean 实例化。ErrorBean 实例被存储在用户的会话中(关于会话的更多信息,请参阅 Jeff Wilson 的文章, GoFor-It 记事,第 3 部分:会话管理,servlet 和维护状态)。下面的代码是 ErrorView 的全部类定义。

ErrorView.java

\** This is the code for ErrorView.java **\

 

public class ErrorView {

        private java.lang.String _fieldMessage = new String();

 

public ErrorView() {

        super();

}

public ErrorView(String message) {

   setMessage(message);        

}

 

public java.lang.String getMessage() {

        return _fieldMessage;

}

 

public void setMessage(java.lang.String message) {

        _fieldMessage = message;

}

}

其它重要的 Java 组件
还有其它几个组件参与了“用户注册”过程。当用户注册时,不仅只调用 JSP 和视图 bean,其它的几个服务器端组件也参与了此过程。

UserDataBean
UserDataBean 是一个 JavaBeans bean,它表示客户端需要的要用于多个请求的用户数据。(关于客户端 bean 和 UserDataBean 的更多信息, 请查阅本系列的最后一篇文章:“Bean” ,完成任务:使用客户端的 Bean 来实现组件的独立,作者 Sandeep Desai。)当用户提交注册表单时,RegistrationServlet 将 UserDataBean 实例化,并向其植入来自表单的数据。然后 UserDataBean 被存储在用户的会话中。

如果在User EJB 试图向数据库中添加用户时出现错误,servlet 会让浏览器转回 Register.jsp,Register.jsp 使用 UserDataBean 为表单上的简单文本域检索值。这些文本域不要求任何进行预先填充的逻辑,只要求一个从 JSP 到 UserDataBean 的简单 getProperty 调用。

RegistrationServlet
RegistrationServlet 在我们的“用户注册”个案研究 中是一个交互控制器。当用户提交注册表单时, RegistrationServlet 被调用。servlet 将一个新 UserDataBean 实例化并向其植入来自表单的数据。然后 servlet 调用适当的对象来处理调用后端对象的方法,将 UserDataBean 作为参数传递。

如果 EJB 向数据库添加用户成功,servlet 会让用户的浏览器转到新页面,该页面上显示一条确认消息。 但是,如果当 EJB 处理请求时遇到错误,servlet 将捕获 EJB 抛出的异常并将 ErrorBean 实例化,封装出错消息。然后 servlet 将让浏览器转回 Register.jsp,显示一条出错消息,并根据存储在 PrefillsRegistrationJSPView 和 UserDataBean 中的值预先填充表单。

UserRegistersCommandBean
作为控制器,调用合适的对象来履行用户的请求是 servlet 的工作。我们的应用使用 EJB 并遵照命令模式(在 IBM 的一本红皮书 Design and Implement Servlets, JSPs, and EJBs for IBM WebSphere Application Server 中详细讨论了命令模式)。(请参阅参考资料)。当用户注册时,RegistrationServlet 调用 UserRegistersCommandBean,UserRegistersCommandBean 知道如何与 EJB 交谈。然后,User EJB,一个实体 bean,执行向数据库实际添加用户的工作。

最后一个,但不是地位最低的: Register.jsp
您已经看到了我们的视图 bean 的类定义,ErrorView 和 PrefillsRegistrationJSPView,那么现在我们来看一下 JSP 如何使用这些 bean。 下面是摘自 Register.jsp 的一段代码。最值得注意的部分用红色标识。 您会注意到第一段红色的代码是每个视图 bean 的 useBean 标记。useBean 标记告诉 JSP 处理器我们将在 JSP 中引用这些 JavaBeans。当 JSP 转换为 servlet,JSP useBean 标记也被转换为对象声明,声明中的对象为标记中指定的 bean 类型。

一旦我们用 useBean 标记建立了 JSP,我们就可以调用 bean 的方法。我们通过使用 JSP setProperty 和 getProperty 标记执行此操作。getProperty 和 setProperty 标记的 name 属性引用我们在 useBean 标记中为我们的 bean 指定的标识。property 属性引用我们正试图在 bean 上访问的属性和关联的方法 - 在此例中是 setInitialTitle() 和 getTitleSelect()。

Register.jsp 摘选



	\** This is an excerpt from Register.jsp**\

. . . 










Please complete the following form:
= a required field
. . .
Title:

如您所见,在 Register.jsp 中无 Java 代码!我们的首席体系架构设计师感到很自豪。Java 开发者也很感激,他们可以不受干扰,继续平静地工作,而 Web 页面开发者则已经高兴得手舞足蹈了,因为他们知道他们可以很容易地更改页面的外观和感觉,而无需在 Java 代码上胡乱花费工夫。又一条“龙”被杀掉了,Go-ForIt 大地上中的一切都安然无恙。

别走开......
请继续关注我们的 Go-ForIt 记事的下一部分,其中我们将讨论输入验证问题。 要查看有关“屠龙”故事的先前的文章,请访问概述。

关于作者
作者照片Allison Pearce Wilson 是设在德克萨斯州奥斯汀的 IBM Developer Relations 组织的电子商务体系架构设计师。作为 IBM Developer Relations 的一个成员, Allison 向全球的 IBM 商业伙伴提供关于 IBM 电子商务框架的技术咨询。她深受传统文科教育的熏陶,这种工作几乎完全与她的教育无关,她是 Smith 大学的文学士。可通过 allison1@us.ibm.com 与她联系。
下载本文示例代码


将 Java 代码与 JSP 组件分离将 Java 代码与 JSP 组件分离将 Java 代码与 JSP 组件分离将 Java 代码与 JSP 组件分离将 Java 代码与 JSP 组件分离将 Java 代码与 JSP 组件分离将 Java 代码与 JSP 组件分离将 Java 代码与 JSP 组件分离将 Java 代码与 JSP 组件分离将 Java 代码与 JSP 组件分离将 Java 代码与 JSP 组件分离将 Java 代码与 JSP 组件分离
阅读(249) | 评论(0) | 转发(0) |
0

上一篇:JSP国外优秀站点

下一篇:用JSP操作Cookie

给主人留下些什么吧!~~