JavaServerFaces.入门,第部分-构建基本应用程序(精品)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
JavaServer Faces 1.2 入门,第1 部分: 构建基本应用程序
Richard Hightower (***********************), CTO, ArcMind
简介: Java™Server Faces(JSF)技术是一种服务器端框架,它提供一种基于组件的Web 用户界面开发方式。
JSF 1.2(集成在Java Enterprise Edition 5 中)纠正了JSF 的一些缺陷并添加了一些出色的特性。
这个教程系列讨论如何使用JSF 1.2。
本系列偏重示例,较少解释理论—这是为了帮助您尽快开始使用JSF。
标记本文!
发布日期: 2008 年 1 月18 日
级别:初级
开始之前
关于本系列
这个教程系列讨论JavaServer Faces(JSF)技术的基础知识。
JSF 是一种用于Java Web 应用程序的服务器端用户界面组件框架。
本系列针对JSF 的新手,帮助他们快速入门—使用JSF 并不是必需的,但是使用JSF 组件可以减少工作量。
本系列只讨论基础知识并提供大量示例。
与AWT、SWT 和Swing 一样,JSF 是一种比较传统的GUI 开发环境。
它的主要好处之一是,它将困难的工作交给框架开发人员而不是应用程序开发人员,从而简化了Web 开发。
坦率地说,JSF 本身比许多其他Web 框架复杂,但是它对应用程序开发人员隐藏了复杂性。
与大多数其他框架相比,用JSF 开发Web 应用程序要容易得多:需要的代码更少,复杂性更低,配置更少。
如果您从事Java 服务器端Web 开发,那么JSF 是最容易掌握的框架。
它非常适合创建Web 应用程序(不是Web 站点本身)。
它让Web 开发人员可以集中精力处理Java 代码,而不需要处理请求对象、会话对象、请求参数或复杂的XML 文件。
与其他Java Web 框架相比,使用JSF 可以更快速地做更多事情。
关于本教程
本教程介绍一种基本的JSF 开发方法。
在本教程中,不使用工具或IDE 支持(尽管工具支持是JSF 的主要好处之一)。
我们将要进行纯粹的编程!我只介绍基本知识,从而帮助您理解这里的讨论内容并有效地使用JSF 构建Web 应用程序。
您会惊奇地发现,即使不使用IDE 工具,JSF 开发也比其他Java Web 框架容易。
目标
在本教程中,我将概述JSF 的特性并讲解如何编写基本的JSF 应用程序。
我将构建一个简单的计算器应用程序,逐步改进它的外观和感觉,修改它的结构来添加依赖性注入,以及实现JSF 的导航机制。
在第2 部分中,将构建定制的转换器、检验器和阶段监听器。
谁应该学习本教程?
如果您是JSF 的初学者,那么本教程正适合您。
如果您用过JSF,但是没有用过JSF 1.2 特性,或者只用GUI 工具构建过JSF 应用程序,那么也可能从本系列教程学到许多知识。
前提条件
本教程适合初级或中级水平的Java 开发人员。
您应该基本了解Java 语言并拥有一定的GUI 开发经验。
系统需求
要运行本教程中的示例,您需要一个Java 开发环境(JDK)和Apache Maven。
拥有Java IDE 会有所帮助。
本教程提供了Maven 项目文件以及Eclipse Java EE 和Web Tools Project(WTP)项目文件。
在下载中可以获得示例代码。
JSF 基础
与Swing 和AWT 一样,JSF 也是一种开发框架,它提供一套标准的、可重用的GUI 组件,用来构建Web 应用程序的界面。
JSF 具有以下优点:
l完全地隔离行为和表示
l能够对有状态进行组件级控制
l能够轻松地将事件连接到服务器端代码
l使用大家熟悉的UI 组件和Web 层概念
l提供多种标准的供应商实现
l出色的IDE 支持
典型的JSF 应用程序由以下部分组成:
l用来管理应用程序状态和行为的JavaBean
l有状态GUI 组件
l事件驱动的开发(像传统GUI 开发一样通过监听器)
l表示Model-View-Controller(MVC)视图的页面;页面通过JSF 组件树引用视图根(view root)
为了使用JSF,可能需要克服一些概念方面的障碍,掌握这些概念是很值得的。
JSF 的组件状态管理、易用的用户输入检验、细粒度的基于组件的事件处理以及可轻松扩展的体系结构,这些概念会大大简化Web 开发。
本节详细解释最重要的JSF 特性。
基于组件的体系结构
有状态组件
在使用JSF 时最大的障碍是,您可能会忘记JSF 是一个有状态组件模型。
如果您以前使用过Struts,就应该牢牢记住:“JSF 不是Struts。
JSF 不是Struts。
”我发现,与多年使用Struts 和没有GUI 组件开发经验的开发人员相比,具备Swing、AWT、Visual Basic 或Delphi GUI 背景的开发人员能够更快地掌握JSF。
本教程将帮助您了解有状态组件的概念。
JSF 为标准HTML 中可用的每个输入字段都提供了组件标记。
还可以针对应用程序特有的用途编写定制的组件,或者用多个HTML 组件组合成一个复合组件—例如,用三个下拉菜单组成一个Data Picker 组件。
JSF 组件是有状态的。
它们的状态是通过JSF 框架提供的。
JSF 使用组件生成HTML 响应。
还可以使用许多第三方JSF GUI 组件。
JSF 包括:
l一个事件发布模型
l一个轻量型反转控制(inversion-of-control,IoC)容器
l用于几乎每种常用GUI 特性的组件,包括(但不限于):
¡可插入的显示
¡服务器端检验
¡数据转换
¡页面导航管理
作为一种基于组件的体系结构,JSF 具有很强的可配置性和可扩展性。
大多数JSF 功能—比如导航和托管bean 查找—都可以替换为可插入的组件。
这种可插入性为构建Web 应用程序GUI 提供了很强的灵活性,并允许轻松地将其他基于组件的技术结合到开发工作中。
例如,可以将JSF 的内置IoC 框架替换为更成熟的IoC/面向方面编程(AOP)Spring 框架,以执行托管bean 查找。
我将在第 2 部分中讨论许多高级特性。
JSF 和JSP 技术
JSF 应用程序的用户界面由JavaServer Pages(JSP)页面组成。
每个JSP 页面包含提供GUI 功能的JSF 组件。
在JSP 页面中,可以使用JSF 定制标记库来显示UI 组件、注册事件处理函数、将组件和检验器关联起来、将组件和数据转换器关联起来等等。
JSF 不了解JSP
JSF 本质上并不直接处理JSP,它通过JSF 标记库处理JSP。
但是,JSF 的生命周期与JSP 的生命周期非常不同。
与JSP 相比,Facelets 能够更好地与JSF 配合,因为Facelets 就是针对JSF 设计的,而集成JSF 和JSP 总是很别扭。
您应该考虑使用Facelets;Facelets 特性将成为JSF 2.0 的组成部分。
关于Facelets 的更多信息请参见参考资料。
JSF 并不一定要使用JSP 技术。
实际上,JSP 页面使用的JSF 标记仅仅引用组件,让它们可以显示。
组件的生命周期与JSP 页面很不一样。
您可以这样体会这一点:在JSP 页面中修改JSF 组件的属性并重新装载页面,这时什么也不会发生。
这是因为标记在它的当前状态中查找组件。
如果组件已经存在了,定制标记就不修改它的状态。
组件模型允许控制器代码修改组件的状态(例如,禁用一个文本字段),当显示视图时,会显示组件树的当前状态。
在典型的JSF 应用程序中,不需要Java 代码,只需要非常少的统一Expression Language(JSTL EL)UI 代码。
正如前面提到的,有许多IDE 工具可以用来构建和组装JSF 应用程序,还有许多第三方JSF GUI 组件。
尽管JSF 在设计时就考虑到了WYSIWYG IDE 工具,但是也可以在不使用WYSIWYG 工具的情况下编写JSF 应用程序(本教程中就是这么做的)。
不需要利用WYSIWYG IDE 支持
尽管JSF 在设计时就考虑到了WYSIWYG IDE 支持,但是不一定非要使用WYSIWYG IDE 支持。
实际上,即使手工编写代码,JSF 仍然比大多数Java Web 框架容易使用。
如果您用Swing 进行编程并使用WYSIWYG IDE,那么可以继续用那个工具进行JSF 编程。
如果您喜欢手工编写Swing 代码,那么也会喜欢手工编写JSF。
纯粹的编程!
JSP 2.1 和JSF 1.2 中的改进
JSP 2.1 添加了许多新特性来支持JSF,包括统一Expression Language(EL)API(JSF 1.2 也添加了这个特性)。
现在可以使用标准的JSTL 标记循环处理列表并显示JSF 组件,这是用JSF 1.1 和JSP 2.0 无法实现的。
关于JSP 2.1 中的改进的更多信息参见参考资料。
(尽管有了这些改进,但是Facelets 仍然更适合JSF,而且JSF 2.0 将融合许多来自Facelets 的思想。
)
JSF 和MVC
JSF 是近几年Java 平台上Web 开发技术迅速发展的成果。
Java Web 开发技术首先从JSP 技术开始,JSP 的主要好处是很容易在HTML(和类似HTML 的)页面中混合Java 代码。
下一步是Model 1 体系结构,它让开发人员将大多数后端代码放到JavaBeans 组件中,然后用<jsp:useBean>标记将JavaBeans 组件导入Web 页面。
这个体系结构对于简单的Web 应用程序很合适,但是许多Java 开发人员讨厌JSP 技术中结合的C++ 特性,比如静态包含。
所以产生了Model 2 体系结构。
从本质上说,Model 2 体系结构是一种用于Web 应用程序的简化版MVC。
在Model 2 体系结构中,控制器由servlet(或Actions)表示,显示由JSP 页面负责。
Apache Struts 是一个简化的Model 2 实现,其中用Actions 替代了servlet。
在Struts 中,应用程序的控制器逻辑与它的数据(由ActionForms 表示)分隔开。
对Struts 的主要批评意见是它太过程性了,不够面向对象(它被戏称为“COBOL for the Web”)。
WebWork 和Spring MVC 是另两种Model 2 体系结构实现,它们降低了过程性,但是它们没有像Struts 那样得到广泛应用。
另外,它们没有像JSF 那样提供有状态组件模型。
(Struts 2 是在WebWork 上构建的,原来的Struts 代码基已经被废弃了。
即使Struts 也对自己不满意。
)
大多数Model 2 框架的真正问题是事件模型太过简单(本质上是高度简化的MVC),而且它没有提供有状态的GUI 组件,因此把太多工作留给了开发人员。
为了简便地创建大多数用户期望的交互方式,需要一个更丰富的组件和事件模型。
与JSP 技术一样,大多数Model 2 框架允许非常容易地在HTML 布局和格式化代码中混合GUI 定制标记,这使代码具备组件那样的松散性,但它们不是有状态的。
而且,一些Model 2 体系结构(比如过去的Struts)在行为和状态的隔离方面犯了错误,这使许多Java 开发人员觉得他们是在使用COBOL。
更丰富的MVC 环境
JSF 不是Struts;抛弃原来的观念,接受新的知识
JSF 不是一种Model 2 框架。
它比Model 2 框架丰富得多。
但是,因为Struts 原来的作者领导了JSF 规范的制订,所以许多人误认为在JSF 项目中可以使用Struts 技能。
不要按照Struts 的经验编写JSF 应用程序。
您必须抛弃一些Struts 技能,学习JSF 技能。
JSF 提供一个组件模型和一个更丰富的MVC 环境—比Model 2 框架丰富得多。
与Model 2 体系结构相比,JSF 更接近真正的MVC 编程环境,尽管它仍然是在一个无状态协议上构建的。
JSF 还有助于构建比Model 2 框架更细粒度的事件驱动的GUI。
JSF 提供了大量事件—选择菜单项、单击按钮、展开树节点等等—而大多数Model 2 框架依赖于简单的“接收请求”事件。
JSF 出色的事件模型让应用程序依赖的HTTP 细节更少,简化了开发工作。
JSF 还改进了传统的Model 2 体系结构,可以更轻松地将表示和业务逻辑移出控制器以及将业务逻辑移出JSP 页面。
实际上,简单的控制器类根本不连接JSF,这使它们更容易测试。
与真正的MVC 体系结构不同,JSF 模型层不太可能发出必须在多个视图中解析的许多事件(但是,Crank 利用JBoss ajax4JSF 的支持尝试这么做;参见参考资料)。
另外,这也是不必要的,因为JSF 使用的是无状态协议。
用来修改或更新视图的系统事件几乎总是一个来自用户的请求。
JSF MVC 实现的细节
在JSF 的MVC 实现中,映射托管bean 在视图和模型之间起协调作用。
因此,一定要限制托管bean 中与JSF 连接的业务逻辑和持久性逻辑。
一种常用的替代方法是将业务逻辑委托给应用程序模型。
在这种情况下,托管bean 还映射模型对象,让视图可以显示它们(作为托管bean 的属性)。
我喜欢将我的托管bean 分为两类:连接JSF 的托管bean(控制器)和不连接JSF 的托管bean(模型对象)。
与JSP 技术不同,JSF 的视图实现是一个有状态组件模型。
JSF 视图由两部分组成:视图根和JSP 页面。
视图根是一个UI 组件集合,它维护UI 的状态。
与Swing 和AWT 一样,JSF 组件使用Composite 设计模式管理一个组件树(简单地说:容器包含组件;容器也是组件)。
JSP page 将UI 组件绑定到JSP 页面,允许将字段组件绑定到后端bean 的属性(更可能是属性的属性),以及将按钮绑定到事件处理函数和动作方法。
图 1 从MVC 的视角展示一个示例应用程序的结构(稍后将详细了解这个程序):
图 1. 示例应用程序的结构
如果第一节的内容让您有点儿困惑,也不必担心:最困难的部分已经过去了。
了解了JSF 的总体框架,实现这种技术的过程就完成了一大半儿—您不久就会发现克服这个障碍是值得的。
现在,理论已经讨论够了:我们来进行纯粹的编程吧!
JSF 示例:逐步创建
本节讨论用JSF 创建应用程序的步骤。
这个示例应用程序是一个简单的计算器应用程序,它演示JSF 技术的以下方面:
l如何安排JSF 应用程序的结构
l如何配置JSF 的web.xml 文件
l如何配置应用程序的faces-config.xml 文件
l编写托管bean(也称为模型对象和控制器)
l使用JSP 技术构造视图
l使用定制的标记库在视图根中构造组件树
l对表单字段的默认检验
在后面几节中,将通过几次迭代改进这个应用程序,让它具备更多JSF 特性。
图 2 给出最终的Calculator 示例应用程序的注解视图。
在下载中可以获得应用程序源代码。
图 2. 最终的Calculator 应用程序
Calculator 应用程序
用Maven 2 和Eclipse WTP 执行构建
Calculator 示例应用程序的默认构建环境是Apache Maven 2。
在这些示例中,我使用Maven Web 应用程序的默认布局。
用Eclipse JEE、Tomcat 和Maven 2 运行本教程源代码的说明和JAR 文件见参考资料。
最初的Calculator 示例应用程序的目标是显示一个页面,让用户输入两个数字,然后将它们相加或相乘。
这个页面包含:
l一个表单
l两个文本字段
l两个标签
l两个错误消息位置
l两个Submit 按钮
l一个结果面板
文本字段用来输入数字。
标签表示文本字段的意义。
错误消息位置用来显示文本字段的检验或数据转换错误消息。
有两个JSP 页面:calculator.jsp 和index.jsp,后者仅仅重定向到calculator.jsp。
一个称为Calculator的托管bean 作为calculator.jsp 的模型。
这个简单的示例没有控制器层。
创建应用程序:概述
为了用JSF 构建最初的Calculator 应用程序,需要:
l在Web 应用程序部署描述符文件(web.xml)中声明Faces Servlet 并添加Faces Servlet 映射
l在web.xml 文件中指定faces-config.xml 文件
l创建Calculator类
l在faces-config.xml 文件中声明Calculator bean
l创建index.jsp 页面
l创建calculator.jsp 页面
这个应用程序使用以下目录结构:
+---src
+---main
+---java
+---webapp
+---pages
+---WEB-INF
+---lib
Java 代码放在src/main/java/ 下面。
web.xml 文件放在src/main/webapp/WEB-INF 目录中。
JSF 配置文件也放在
src/main/webapp/WEB-INF 下面。
这个示例应用程序是用Eclipse IDE for Java EE Developers(Eclipse JEE)创建的,这个IDE 包含一个JSF 项目创建向导,它会创建包含适当条目的web.xml 文件和faces-config.xml 文件。
我假设您将使用支持Java EE 5 的应用服务器,也就是说,它有JSF 和JSTL JAR 文件。
关于设置Tomcat 来运行JSF、设置Eclipse JEE 来运行Tomcat 6 并在Maven 2 中运行示例的说明,参见参考资料。
声明Faces Servlet 和servlet 映射
为了使用Faces Servlet,首先需要在web.xml 文件中声明它,见清单1:
清单1. web.xml 中的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>
这与大多数web.xml 描述符相似,但是要让JSF servlet 处理请求,而不是指定自己的servlet。
对使用<f:view>标记(就像这个示例应用程序所做的)的JSP 文件的所有请求必须通过这个servlet。
因此,需要添加一个映射并通过这个映射只装载启用JSF 的JSP 页面,见清单2:
清单2. web.xml 中的Faces Servlet 路径映射
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
清单2 让Faces Servlet 容器将所有以/faces/ 开头或以*.jsf 结尾的请求发送到Faces Servlet 进行处理。
这让JSF 在显示JSF 页面之前初始化Faces 上下文和视图根。
视图根包含JSF 组件树。
Faces 上下文是与JSF 进行交互的方式。
这意味着,要想装载Calculator 应用程序,应该使用http://localhost:8080/calculator/pages/calculator.jsf 或
http://localhost:8080/calculator/faces/pages/calculator.jsp —而不是http://localhost:8080/calculator/pages/calculator.jsp。
如果在JSF 上下文之外装载JSP 页面,JSF 就没有机会初始化Faces 上下文或视图根。
指定faces-config.xml 文件
如果将Faces 配置文件命名为faces-config.xml 并把它放在Web 应用程序的WEB-INF 目录中,那么Faces Servlet 会自动地找到并使用它(因为这是默认设置)。
另外,也可以在web.xml 文件中的初始化参数
javax.faces.application.CONFIG_FILES 中指定逗号分隔的文件列表,从而装载一个或多个应用程序配置文件。
除了最简单的JSF 应用程序之外,对于所有JSF 应用程序,很可能会使用第二种方法。
因为这个示例应用程序很简单,我们使用默认的faces-config.xml 文件位置/src/main/webapp/WEB-INF。
创建Calculator类
现在,创建一个称为Calculator的POJO(普通Java 对象),它根本不连接到JSF。
然后用方法绑定和属性绑定将它绑定到JSF。
这个简单的类有两个属性:firstNumber和secondNumber。
我的目标是演示如何开始使用JSF,所以让模型对象尽可能简单。
这个应用程序的模型包含在一个模型对象中,见清单3。
后面将把它分割成两个类:控制器和模型。
清单3. Calculator POJO
package com.arcmind.jsfquickstart.model;
/**
* Calculator. Simple POJO.
*
* @author Rick Hightower
*/
public class Calculator {
/** First number used in operation. */
private int firstNumber = 0;
/** Result of operation on first number and second number. */ private int result = 0;
/** Second number used in operation. */
private int secondNumber = 0;
/** Add the two numbers. */
public void add() {
result = firstNumber + secondNumber;
}
/** Multiply the two numbers. */
public void multiply() {
result = firstNumber * secondNumber;
}
/** Clear the results. */
public void clear() {
result = 0;
}
/* ---------- properties ------------- */
public int getFirstNumber() {
return firstNumber;
}
public void setFirstNumber(int firstNumber) {
this.firstNumber = firstNumber;
}
public int getResult() {
return result;
}
public void setResult(int result) {
this.result = result;
}
public int getSecondNumber() {
return secondNumber;
}
public void setSecondNumber(int secondNumber) {
this.secondNumber = secondNumber;
}
}
清单3 非常简单,不需要解释;您只需阅读代码。
但是要记住,Calculator POJO 并不处理JSF。
在faces-config.xml 文件中声明Calculator bean
清单4 给出完整的faces-config.xml 文件。
可以看到,这个文件与Java EE JSF XML 模式相关联。
在faces-config.xml 中,使用<managed-bean>元素声明一个bean,JSF 可以绑定到这个bean:
清单4. 包含托管bean 声明的faces-config.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="/xml/ns/javaee"
xmlns:xsi="/2001/XMLSchema-instance"
xsi:schemaLocation="/xml/ns/javaee
/xml/ns/javaee/web-facesconfig_1_2.xsd"
version="1.2">
<managed-bean>
<managed-bean-name>calculator</managed-bean-name>
<managed-bean-class>com.arcmind.jsfquickstart.model.Calculator</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
</faces-config>
清单4 中的bean 声明用<managed-bean-name>元素指定bean 的名称calculator。
还用<managed-bean-class>指定完全限定的类名。
这个类必须有一个无参数构造函数。
<managed-bean>元素的<managed-bean-scope>子元素指定JSF 可以在哪里找到这个bean:request范围。
如果将这个bean 名称绑定到一个视图(本教程后面会这么做),而且JSF 无法找到它,那么JSF 就会创建它。
这是通过JSF 和统一EL API 实现的。
request范围只针对一个请求。
这是放置不需要在页面视图之间维持状态的bean 的合适位置。
创建index.jsp 页面
在Calculator 应用程序中,index.jsp 页面的用途是确保calculator.jsp 页面装载JSF 上下文,让页面能够找到对应的视图根。
清单 5 给出index.jsp 页面:
清单5. index 页面重定向到calculator.jsp
<jsp:forward page="/faces/calculator.jsp" />
这个页面仅仅把用户重定向到faces Web 上下文中的calculator.jsp。
这将calculator.jsp 页面放在JSF 上下文路径下面,它可以在这里找到它的视图根。
创建calculator.jsp 页面
calculator.jsp 页面是Calculator 应用程序视图的核心。
这个页面接受用户输入的两个数字,见图3:
图 3. 在Eclipse JEE/WTP 中运行的第一个Calculator 应用程序
这个页面的完整代码见清单6:
清单6. /src/main/webapp/calculator.jsp
<?xml version="1.0" encoding="ISO-8859-1" ?>
<%@ taglib uri="/jsf/html" prefix="h"%>
<%@ taglib uri="/jsf/core" prefix="f"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="/1999/xhtml">
<head>
<title>Calculator Application</title>
</head>
<body>
<f:view>
<h:form id="calcForm">
<h4>Calculator</h4>
<table>
<tr>
<td><h:outputLabel value="First Number" for="firstNumber" /></td>
<td><h:inputText id="firstNumber"
value="#{calculator.firstNumber}" required="true" /></td>
<td><h:message for="firstNumber" /></td>
</tr>
<tr>
<td><h:outputLabel value="Second Number" for="secondNumber" />
</td>
<td><h:inputText id="secondNumber"
value="#{calculator.secondNumber}" required="true" /></td>
<td><h:message for="secondNumber" /></td>
</tr>
</table>
<div>
<h:commandButton action="#{calculator.add}" value="Add" />
<h:commandButton action="#{calculator.multiply}" value="Multiply" />
<h:commandButton action="#{calculator.clear}" value="Clear" immediate="true"/>
</div>
</h:form>
<h:panelGroup rendered="#{calculator.result != 0}">
<h4>Results</h4>
<table>
<tr><td>
First Number ${calculator.firstNumber}
</td></tr>
<tr><td>
Second Number ${calculator.secondNumber}
</td></tr>
<tr><td>
Result ${calculator.result}
</td></tr>
</table>
</h:panelGroup>
</f:view>
</body>
</html>
注意,这个文件中的大多数代码是普通的HTML(准确地说,是XHTML)。
可以在<f:view>、<h:form>和
<h:panelGroup>标记中使用HTML。
一种常见的误解是,不能在JSF 标记中混合HTML。
实际上,在许多情况下都可以这么做。
但是,不能在<h:commandButton>中使用HTML,因为这个标记只接受其他组件作为子元素。
因为这个页面有点儿复杂,我来解释一下如何构建它。
声明标记库
首先声明JSF 的标记库,见清单7:
清单7. 将标记库导入calculator.jsp
<%@ taglib uri="/jsf/html" prefix="h" %>
<%@ taglib uri="/jsf/core" prefix="f" %>
清单7 告诉JSP 引擎您希望使用两个JSF 标记库html和core。
html标记库包含用来处理表单和其他HTML 相关元素的所有标记。
core标记库包含JSF 特有的所有逻辑、检验、控制器和其他标记。
<f:view>标记
在用一般的HTML 布置页面之后,要告诉JSF 系统您希望使用JSF 管理组件。
这需要使用<f:view>标记,这个标记告诉容器希望使用JSF 管理其中的组件。
如果没有<f:view>,JSF 就无法构建组件树,以后也无法搜索已经创建的组件树。
使用<f:view>标记的方式见清单8:
清单8. calculator.jsp 的<f:view>标记
<f:view>
<h:form id="calcForm">
...
</h:form>
</f:view>
清单8 中的第一行是<f:view>的声明,它告诉容器它由JSF 管理。
在<f:view>标记中:<h:form>标记
清单8中的第二行是<h:form>标记,这告诉JSF 这里需要一个HTML 表单。
在显示阶段,会搜索这个表单组件中包含的组件并要求它们显示自己,这时它们会生成标准的HTML。
可以按照您喜欢的任何方式布置表单组件。
清单9 是Calculator 应用程序的输入字段的布局:
清单9. 在calculator.jsp 的<h:form>标记中:输入字段
<table>
<tr>
<td><h:outputLabel value="First Number" for="firstNumber" /></td>
<td><h:inputText id="firstNumber"
value="#{calculator.firstNumber}" required="true" /></td>
<td><h:message for="firstNumber" /></td>
</tr>
<tr>
<td><h:outputLabel value="Second Number" for="secondNumber" />
</td>
<td><h:inputText id="secondNumber"
value="#{calculator.secondNumber}" required="true" /></td>
<td><h:message for="secondNumber" /></td>
</tr>
</table>
请再次注意,这里使用了大量HTML。
可以用span、div、table或其他元素布置应用程序的布局。
JSF 对设计人员没什么限制。
一些工具甚至允许在Dreamweaver 中使用JSF(参见参考资料)。
在结合使用JSF 和Facelets 时,JSF 甚至对设计人员更友好(可以在JSF 1.1 和更高版本中使用Facelets,而且它将成为JSF 2.0 的组成部分)。
在清单9 中还要注意,两个inputText的value属性都使用JSF EL(JavaServer Faces Expression Language)值表达式(例如,value="#{calculator.firstNumber}")。
初看上去这很像JSTL EL。
但是,统一EL 代码实际上将字段与对应的后端bean 的属性值关联起来。
这种关联是双向的;也就是说,如果firstNumber的值是100,那么在显示表单时会显示100。
同样,如果用户提交了一个有效的值,比如200,那么200 会成为firstNumber属性的新值(假设通过了转换和检验过程,稍后讨论这个问题)。
除了字段之外,calcForm还通过三个commandButton与三个动作相关联,见清单10:
清单10. 在calculator.jsp 的<h:form>标记中:按钮
<div>
<h:commandButton action="#{calculator.add}" value="Add" />
<h:commandButton action="#{calculator.multiply}" value="Multiply" />
<h:commandButton action="#{calculator.clear}" value="Clear" immediate="true"/>
</div>
关于样式表
所有JSF 组件的外观和感觉都是通过样式表类声明的。
每个组件有一个样式与行内样式相关联,有一个styleClass将组件与一个styleSheet类关联起来。
<panelGrid>(将在下一节中使用)还有对行和列应用样式的样式属性。
在下一节中,将使用Cascading Style Sheets(CSS)控制JSF 的样式。
清单10 中的代码将三个按钮绑定到calculator类的add()、multiply()和clear()方法,所以当单击按钮时会调用对应的方法(假设成功执行了转换和检验)。
在默认情况下,在执行任何动作方法(比如add()或multiply())之前,JSF 会检验表单。
但是,如果使用immediate="true"(就像清单10 中的Clear 按钮那样),那么JSF 会跳过检验阶段并直接执行方法(稍后会进一步讨。