爱心技术专栏专题

初识JavaServerFaces(第1部分)

摘录:java基础 来源:java基础 加入时间:2007年03月08日
摘要:
初识JavaServerFaces(第1部分)

摘要

2002年9月,在Java Specification Request(JSR)127上发布了JavaServer Faces规范的早期访问(early access,EA)草案。JavaServer Faces具有定义良好的请求处理生命周期和丰富的组件层次结构,将会深远地影响到J2EE(Java …

转载:转载请保留本信息,本文来自
http://www.51dibs.com
/html/2006/article/info8/a_da9a2ee4d6b33345.htm

初识JavaServerFaces(第1部分)

站点:爱心种子小博士 关键字:初识JavaServerFaces(第1

   
初识JavaServerFaces(第1部分)

摘要

2002年9月,在Java Specification Request(JSR)127上发布了JavaServer Faces规范的早期访问(early access,EA)草案。JavaServer Faces具有定义良好的请求处理生命周期和丰富的组件层次结构,将会深远地影响到J2EE(Java 2 Platform, Enterprise Edition)应用程序的开发。本系列文章篇分为两部分,在第 1 部分中,David Geary 将介绍 JavaServer Faces并初步探讨它的基本概念。(2002年11月29日)

正文

最近,在使用Struts、Enterprise JavaBeans (EJB)、servlets, JavaServer Pages (JSP)和JSP标准标签库(JSP Standard Tag Library,JSTL)开发一个复杂的Web应用程序时,我有幸参与培训和指导了一群 Java的开发新手。结果,项目成功了,且在预算计划内按时交付。另外,还添加了许多最初没有设想到的特性。如您所想像到的,在开发过程中,我们面对了很多技术上的挑战;主要有:

  1. 实现自定义组件,包括一个树/表查看程序和一个查询生成器,让用户可动态地添加和删除基本组件,如文本字段和下拉列表,用以生成数据库查询。
  2. 支持手持设备,比如PDA和无线电频率装置。
  3. 缺少集成开发环境,无法有效进行快速应用程序开发(rapid application development,RAD)。

我们花了大量的时间和精力来实现自定义组件和支持手提设备,特别是后者。还有,尽管一些开发人员使用Eclipse开放源代码IDE,但我们还是缺少有效的RAD工具来实现Web应用程序的用户界面。.

除非您这些年来一直将自己封闭在一个小天地内,否则我敢保证您一定知道,现有的很多工具都可用于创建自定义Web组件以及支持HTML以外的标记语言,所有的这些都封装在一个非常完美的IDE中。当然软件是带有WebForms的Microsoft .Net;IDE是Visual Studio。

尽管有这些吸引人的.Net特性,像当今很多软件开发商一样,我所工作的公司还是选择跟随J2EE,因为它是平台和供应商无关的,而且有很多可用的Java和J2EE开放源代码软件。

如果您可以利用Java和.Net的最佳特性、平台和供应商无关性、开放源代码软件(如Ant和log4j),以及易于创建自定义Web组件并将它们移植到多个设备的能力(所有这一切都包装在一个killer IDE中),这难道不是很好吗?JavaServer Faces恰恰可以做到这一切。

请阅读整个"初识JavaServer Faces"系列:

    第1部分: 学习如何使用JSF实现Web用户界面

    第2部分: 探讨JavaServer Faces组件

注意:您可以从参考资料下载本文的源代码。

什么是JavaServer Faces?

JavaServer Faces (JSF)是一种应用程序框架,用于创建基于Web的用户界面。如果您熟悉Struts(一种流行的开放源代码的基于JSP的Web应用程序框架)和Swing(针对桌面应用程序的标准Java用户界面),就可以将JavaServer Faces想像成这两种框架的组合。与Struts一样,JSF通过一个控制器servlet来提供Web应用程序生命周期管理;也与Swing一样,JSF提供了一个带有事件处理和组件呈现(rendering)的丰富组件模型。

简言之, JSF减轻了开发基于Web的应用程序的工作量,因为它:

  • 可以通过一组标准的、可重用的服务器端组件来创建用户界面;
  • 提供了一组JSP标签以访问这些组件;
  • 在表单重新显示时,透明地保存状态信息并重新填充表单;
  • 提供了实现自定义组件的框架;
  • 封装了事件处理和组件呈现,以便您可以使用标准的JSF组件或自定义组件来支持除HTML之外的标记语言;
  • 让工具开发商可以开发针对标准Web应用程序框架的IDE。

除了在概念上组合Struts和Swing之外,JSF还是Microsoft WebForms的直接竞争者。这两个框架在概念上和实现上都非常相似。因为JSF代表了基于Java的Web应用程序框架的标准,所以工具开发商可以集中精力使用JSF开发IDE,而不必针对现有的约35种基于Java的Web应用程序框架中的某种框架开发IDE。

注意: Struts开发人员不必担心;尽管JSF和Struts有很多共同之处,但JSF不会舍弃Struts。请查阅参考资料,那里关于Struts和JavaServer Faces集成策略的讨论。

目前,JSF是一个EA版本,因此它还不成熟。规范中一些功能没有指定,目前规范和参考实现(reference implementation)没有同步,在前者中指定了新的语法和功能,但在后者中却没有实现。然而对于编写代码来说,JSF已经是足够成熟了(尽管很多代码可能会过时,参见下面的"免责声明"),而且参考实现是相当完整的,漏洞也相对比较少。您可以从参考资料下载JSF规范、参考实现、两个示例程序以及JSF教程。

本系列的两篇文章提供了侧重于JavaServer Faces代码实现的介绍。在本文中,我从简短地讨论JSF生命周期来开始,然后研究一些示例代码,用以展示如何使用JSF来实现基于Web的用户界面及如何利用内置的有效性验证。在第 2 部分,我将阐述一些更加高级的JSF概念,如实现自定义验证、使用模型对象、国际化、创建自定义组件,并在最后介绍了委派事件处理和呈现, 这样您可以使用组件来生成除HTML之外的标记语言。

JavaServer Faces生命周期

JSF使用7个不同阶段来处理HTTP请求,如图1所示。正常的控制流使用实线表示,而虚线表示了其他的控制流,这些控制流取决于组件是请求一个页面重新显示,还是发生了有效性验证或是转换错误。

点击查看实际大小的图片

图1. JavaServer Faces生命周期。

Reconstitute Request Tree阶段为所请求的页面创建一个组件树。如果那个页面以前显示过并且JSF保存过该页面的状态信息,那么状态信息就会添加到请求中。这表明一个表单重新显示时,JSF将自动保存该表单的状态信息,比如用户没有正确填写表单的情况。这种有用的特性是Microsoft的WebForms提供的一个基本能力,但在J2EE中却找不到该特性。

在Apply Request Values阶段,JSF实现在组件树中的组件上进行迭代(iterate),并调用每个组件的decode()方法。该方法从请求中提取信息并将其存储在该组件中。作为一种选择,组件也可委托呈现程序(renderer)来做解码工作。

在Apply Request Values阶段,除了解码请求信息之外,组件及其呈现程序可能创建请求事件。通常,请求事件会以信号显示出一个或更多的组件的可见的状态改变。例如,单击树形控件的一个节点就可以展开该树的一个分支。另外,一个组件中的事件可能会更新其他组件的可见表示。例如,单击树中的一个叶节点,可能使一个相关的列表改变其内容并重新显示。不管是哪种情况,都会生成一个请求事件并将事件添加到JSF上下文中。

在Apply Request Values阶段生成的请求事件是在Handle Request Events阶段进行处理的。在Handle Request Events阶段,JSF实现调用了具有一个或多个请求事件的各个组件的processEvents()方法。这些组件可以自己处理请求事件或选择将事件处理委派给一个事件处理程序。processEvents()方法是一个boolean()方法。如果该方法返回false,生命周期处理就进入Process Validations阶段。否则,生命周期处理就直接进入Render Response阶段。

在Reconstitute Request Tree阶段,JSF实现可以为组件树中任何组件注册一个或多个验证程序。在Process Validations阶段,JSF实现调用每个验证程序的validate()方法。这些验证程序接着执行正确性检查并从它们的validate()方法中返回一个boolean值。如果该方法返回true,JSF生命周期就正常进行。否则,JSF实现就直接进入Render Response阶段。

每个JSF用户界面组件可以与Java对象(也称为模型对象)中的一个字段关联起来。在Update Model阶段,这些组件值会复制到该组件的模型对象。组件的updateModel()方法承担那些数据的传输。在该阶段中可能发生转换错误,因为请求参数是字串符,但模型值可以表示任何类型的Java对象。如果发生了转换错误,JSF实现就会直接进入Render Response阶段。

在JSF应用程序中,如果您提交了一个表单或单击了一个链接(两者都必须用JSF组件表示),JSF实现就会分别创建一个表单事件或一个命令事件。这些事件是在Invoke Application阶段通过应用程序相关的处理程序进行处理。这些处理程序一般会指定一个URL,然后JSF实现将请求转发到该URL。目前,特定于应用程序的处理程序只在单个方法中处理表单和命令事件,且一般带有一个switch语句。JSF专家组注意到了这种方法的缺陷,因此几乎可以肯定会在JavaServer Faces 1.0版本中做修改。

最后,Render Response阶段创建一个响应组件树并转发该响应。当用户提交一个表单,单击一个链接,或生成了一个请求,生命周期就重新开始。

既然我们对JavaServer Faces有了一个整体认识,并对JSF生命周期有一个基本的理解,那就让我们来看一些代码吧!

一个简单的JavaServer Faces例子

图2a、2b和2c展示了一个非常简单的JSF应用程序。应用程序的开始页面包含了启动应用程序的链接。该链接指向显示一个简单表单的JSP页面。因为这个简单的应用程序没有执行有效性验证,因此您可以不用填写用户名和密码字段而单击Log In按扭,然后将页面重定向到另一个欢迎您进入JavaServer Faces的JSP页面。

点击放大

图2a. 一个简单的JavaServer Faces应用程序。

点击放大

图2b. JavaServer Faces登录页面。

点击放大

图2c. Welcome to JavaServer Faces页面。

首先,让我们大体上探讨一下实现这个简单应用程序和JavaServer Faces应用程序的细节问题。JSF要求在WEB-INF/lib目录中包含下列的jar文件:

  • WEB-INF/lib/commons-beanutils.jar
  • WEB-INF/lib/commons-collections.jar
  • WEB-INF/lib/commons-digester.jar
  • WEB-INF/lib/commons-logging-api.jar
  • WEB-INF/lib/jsf-api.jar
  • WEB-INF/lib/jsf-ri.jar
  • WEB-INF/lib/jstl.jar
  • WEB-INF/lib/standard.jar

上面列出了JSF应用程序所需的全部jar文件。如我们马上就要看到的那样,尽管JSF应用程序一般使用由JSF实现的JSP标签,但没有独立的标签库描述符(tag library descriptor ,TLD)文件,因为那些信息包含在jar文件中。

下面是组成图2中展示的应用程序的其他文件列表:

  • WEB-INF/web.xml
  • WEB-INF/classes/com/sabreware/listeners/SimpleContextListener.java
  • WEB-INF/classes/com/sabreware/appHandlers/SimpleApplicationHandler.java
  • /index.html
  • /index.jsp

示例1列出了部署描述符(deployment descriptor)(WEB-INF/web.xml)。

示例1. WEB-INF/web.xml

<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC
  "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
   <!-- Context Listener creates and sets the application handler -->
   <listener>
      <listener-class>
         com.sabreware.listeners.SimpleServletContextListener
      </listener-class>
   </listener>

   <!-- Faces Servlet -->
   <servlet>
      <servlet-name>Faces Servlet</servlet-name>
      <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
   </servlet>

   <!-- Faces Servlet Mapping -->
   <servlet-mapping>
      <servlet-name>Faces Servlet</servlet-name>
      <url-pattern>/faces/*</url-pattern>
   </servlet-mapping>

   <welcome-file-list>
      <welcome-file>index.html</welcome-file>
   </welcome-file-list>
</web-app>

上面列出的部署描述符声明了4件事情:

  1. 一个servlet上下文监听器。
  2. 一个控制器servlet 。
  3. 控制器servlet的一个映射 。
  4. 一个欢迎文件。

示例1列出的部署描述符将JSF控制器servlet与URL /faces/*关联起来,该URL使得servlet容器把所有以/faces开始的URL映射到JSF控制器servlet。JSF使用该控制器servlet来控制JSF的生命周期。

示例2中列出了servet上下文监听器。

示例 2. WEB-INF/com/sabreware/listeners/SimpleServletContextListener

package com.sabreware.listeners;

import javax.servlet.*;
import javax.faces.*;
import javax.faces.lifecycle.*;
import com.sabreware.appHandlers.SimpleApplicationHandler;

public class SimpleServletContextListener implements ServletContextListener {
   public void contextInitialized(ServletContextEvent e) {
      LifecycleFactory factory = (LifecycleFactory)
        FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);

      Lifecycle lifecycle = factory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);

      lifecycle.setApplicationHandler(new SimpleApplicationHandler());
   }
   public void contextDestroyed(ServletContextEvent e) {
      // Nothing to do here
   }
}

Servlet容器在应用程序启动时创建了servlet上下文监听器,并调用了监听器的contextInitialized()方法。当应用程序关闭时,servlet容器会调用监听器的 contextDestroyed()方法。上面列出的servlet上下文监听器创建了一个应用处理程序并将它关联到JSF生命周期。应用处理程序处理应用程序事件,并指定JSF实现随后将要转向的URL,参阅在示例2中创建的、在示例3中列出的应用处理程序。

示例3. WEB-INF/com/sabreware/appHandlers/SimpleApplicationHandler

package com.sabreware.appHandlers;

import javax.faces.FactoryFinder;
import javax.faces.context.FacesContext;
import javax.faces.event.FacesEvent;
import javax.faces.lifecycle.ApplicationHandler;
import javax.faces.tree.TreeFactory;

public class SimpleApplicationHandler implements ApplicationHandler {
   public boolean processEvent(FacesContext context, FacesEvent facesEvent) {
      TreeFactory treeFactory = (TreeFactory)FactoryFinder.
        getFactory(FactoryFinder.TREE_FACTORY);
      context.setResponseTree(
        treeFactory.getTree(context.getServletContext(),
                            "/welcome.jsp"));

      return true;
   }
}

在JSF生命周期的Render Response阶段,JSF实现转到一个URL,该URL代表了一个组件树(也就是响应树)。上面列出的应用处理程序将响应树设置到 /welcome.jsp,随后在调用应用处理程序(在JSF生命周期的 Invoke Application阶段)后,JSF实现转到了那个URL。这个简单的示例只生成一个应用程序事件--单击Log In按扭时生成一个表单事件。JSF应用程序可以生成两种类型的应用程序事件:表单事件和命令事件。命令事件是在单击链接时生成的。

示例2中列出的servlet上下文监听器和示例3中列出的应用处理程序是联合起来工作的。上下文监听器创建应用处理程序,然后应用处理程序指定提交登录表单时JSF实现要转向的那个URL。

示例4中列出了/index.html欢迎文件。

示例4. /index.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
   <head>
      <title>A Simple JavaServer Faces Application</title>
   </head>

   <body>
      <font size=4>Welcome to a simple JavaServer Faces Application</font>
      <p>
      <a href=faces/index.jsp>Click here to start the application</a>
   <body>
</html>

上面列出的欢迎文件创建了一个到该应用程序显示的起始JSP页面的链接。所有的JSF应用程序都必须通过JSF控制器servlet路由应用程序显示的起始JSP页面。您可以提供一个包含到那个JSP页面的链接的HTML页面(如本例所展示的),或者让用户输入正确的URL来启动该应用程序。不过,这两种办法都不是太理想,但有望JSF 1.0会提供一种免去这种要求的机制。

示例5列出了该应用程序的初始页面。

示例5. /index.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
   <head>
      <title>A Simple JavaServer Faces Application</title>
   </head>

   <body>
      <%@ taglib uri="http://java.sun.com/j2ee/html_basic/" prefix="faces" %>

      <font size="4">Please enter your name and password</font>
      
      <faces:usefaces>
         <faces:form id="simpleForm" formName="simpleForm">
            <table>
               <tr>
                  <td>Name:</td>
                  <td><faces:textentry_input id="name"/></td>
               </tr>

               <tr>
                  <td>Password:</td>
                  <td><faces:textentry_secret id="password"/></td>
              </tr>
            </table>

            <p><faces:command_button id="submit" commandName="Log In"/>
         </faces:form>
      </faces:usefaces>
   </body>
</html>

前述的JSP页面是简单应用程序中多数动作发生的场所。JSF为其支持的所有标准组件提供了JSP标签。这些标签必须包含在 标签的主体中。上面列出的JSP页面使用了标签以及标签。前一个标签用于创建一个HTML表单,后两个表单分别用于呈现HTML文本元素和HTML密码元素。该JSP页面也使用了标签,该标签用于呈现一个HTML的Submit按钮。

当前述的JSP中的表单提交时,JSF生命周期就开始了,随后调用了应用处理程序。该处理程序将 /welcome.jsp指定为响应树,随后JSF实现转到了那个JSP页面(如示例6所列出的)。

示例6. /welcome.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
   <head>
      <title>A Simple JavaServer Faces Application</title>
   </head>

   <body>
      Welcome to JavaServer Faces!
   </body>
</html>
既然我们已经看到这个简单的JSF应用程序是如何工作的,就让我们通过添加字段有效性验证来扩展这个应用程序。 

JavaServer Faces支持的有效性验证

JavaServer Faces提供了内置的有效性验证和一个用于创建自定义验证程序的框架。内置有效性验证将在下面讨论;自定义有效性验证将在第2部分中讨论。

内置有效性验证

JavaServer Faces提供了下面一些内置验证程序:

  • DoubleRangeValidator
  • LengthValidator
  • LongRangeValidator
  • RequiredValidator
  • StringRangeValidator

前述的验证程序清单代表了类名。这些类驻留在javax.faces.validator包中。DoubleRangeValidator和LongRangeValidator分别用于验证一个请求参数(总是一个字符串)是否可以转换成一个 double或long,以及这些值是否处在指定的范围。LengthValidator检查请求参数的字串长度是否超出了最大值和最小值。RequiredValidator要求指定的字段必须是一个非null值。 StringRangeValidator将一个字串转换成一个long或一个double,并检查其值是否超出了指定的最大值和最小值。

图3展示了运行中的长度验证程序。

点击放大

图3. JavaServer Faces内置有效性验证。

图3展示了由长度验证程序生成的一条错误消息。最小长度指定为3,但相应字段中输入的值只有两个字符,因此当那个字段的相应表单提交时,就会生成一个验证错误及相应的错误消息。

示例7列出了图3中展示的JSP页面。

示例7. 使用LengthValidator

<!DOCTYPE HTML PUBLIC -//W3C//DTD HTML 4.0 Transitional//EN>
<html>
   <head>
      <title>A Simple JavaServer Faces Application</title>
   </head>

   <body>
      <%@ taglib uri=http://java.sun.com/j2ee/html_basic/ prefix=faces %>

      <font size=4>Please enter your name and password</font>

      <faces:usefaces>
         <faces:form id=simpleForm formName=simpleForm>
            <table>
               <tr>
                  <td>Name:</td>
                  <td>
                     <faces:textentry_input id=name>
                        <faces:validator
                          className=javax.faces.validator.LengthValidator/>
                           <faces:attributename=
                             javax.faces.validator.LengthValidator.MINIMUM
                              value=3/>
                      </faces:textentry_input>
                  </td>

                  <td>
                     <faces:validation_message componentId=name/>
                  </td>
               </tr>

               <tr>
                  <td>Password:</td>
                  <td>
                     <faces:textentry_secret id=password/>
                  </td>
               </tr>
            </table>

            <p><faces:command_button id=submit commandName=Log In/>
         </faces:form>
      </faces:usefaces>
   </body>
</html>

如前述JSP页面表明,JSF验证程序是易于使用的:只需在想要验证的组件的主体中添加一个标签和一个或多个标签。当有效性验证失败时,验证程序就在JSF上下文中存储错误消息。您可以使用来提取这些错误消息,它可让您指定错误消息要应用的组件。

JSF后续

JSF代表了一种开发J2EE应用程序的新模式。它具有定义良好的请求处理生命周期、事件处理、有效性验证以及一种用于开发可移植到多种设备的复杂的自定义组件的组件层次结构,这将会大大方便J2EE应用程序Web层的开发。

本文中,我介绍了一些基本的JavaServer Faces概念,包括JSF生命周期、使用JSF标准组件和其相应JSP标签,以及内置的有效性验证。在第2部分中,我将讨论更加高级的JSF特性,包括自定义有效性验证、国际化、使用模型对象以及实现自定义组件。

关于作者

David Geary是Core JSTL Mastering the JSP Standard Tag Library (Prentice Hall,2002; ISBN: 0131001531)、 Advanced JavaServer Pages (Prentice Hall,2001; ISBN:0130307041)和Graphic Java系列(Sun Microsystems Press)的作者。18年来,David一直使用多种面向对象语言开发面向对象的软件。从GOF Design Patterns一书于1994年出版以来,David就是一位设计模式的积极倡议者,并在Smalltalk、C++和Java中使用了一些已实现的设计模式。1997年,David开始成为一个专职的作者和业余的演讲者和顾问。David是定义JSP标准标签库专家组的一名成员,也是Apache Struts JSP框架的贡献者。他主持编写JavaWorld的 Java Design Patterns专栏。

免责声明

本文讨论的代码是针对EA2 JSF参考实现编写的。如前所述,规范和参考实现处在不固定状态,因此,本文中的代码在不远的将来可能会过时;然而,这些代码可作为EA2参考实现的广告,它们分别在Tomcat 4.0.6和Resin 2.1.6上测试通过。此外,您可以一直阅读JSF规范,直到它成熟为止,但要真正掌握这些概念,还必须反复琢磨这些代码。


客户服务中心信箱:[email protected] [email protected] 网站地图

声明

合作伙伴: