在创建服务器端应用程序的用户界面时,有多种可供选择的技术。使用servlet或JSP的Java开发人员通常依靠HTML用户界面组件来开发用户界面,这主要是因为HTML用户界面组件要得到各种Web浏览器的支持。当然,这也意味着与独立的胖客户端程序相比,这种Web应用程序不会拥有丰富的用户界面,因而其功能更少,可用性也更差。可以使用Applet来开发功能丰富的用户界面,不过页面开发人员可能并不熟悉(或者没有兴趣学习)Java语言。
另外,如果参与过大规模的Web系统的开发,你可能已经经历过很多技术性的挑战。例如如何实现客户组件,像查询建立器,或是为数据库查询实现表浏览器。建立这样的客户组件要求有这方面专家,并且要花大量的时间建立和测试这些新的库。在理想的环境中,开发人员应该可以使用已经预先建立,经过测试,并且高度可配置的组件,使之集成在他们的开发环境中。
JavaServer Faces是一种服务器端技术,用于开发具有丰富用户界面的Web应用程序。使用JavaServer Faces ,像创建客户界面组件之类的挑战可以得到解决。这是因为JavaServer Faces技术是一种用户界面框架,用来建立运行在服务器端并将用户界面返回给客户端的Web应用程序。这就对了,用户界面代码运行在服务器上,对客户端产生的事件作出响应。
现在还有一些其他的选择可以建立丰富的服务器端用户界面,如Flash,Swinglets,以及Jade。然而,这些解决方案是私有的,并且支持这些开发的工具通常只能从某个唯一的供应商那里获得。与之不同的是,JavaServer Faces是一个标准,这意味着开发人员不会被锁定在某一个工具供应商上。在Java社区中,规范专家组由来自所有主要的工具供应商的专家组成。因而,开发人员不会缺少工具,并且更有可能获得现在使用的工具的升级版本。当工具供应商在规范上进行合作时,他们将在工具实现上进行竞争。开发人员将从中受益,供应商们会提供各种客户组件和更多的特性供他们选择。
JavaServer Faces技术基于模式-视图-控制器(Model View Controller ,MVC),以便将逻辑和表示分离。如果你已经在这方面有了经验,那么对于JavaServer Faces可能会有某种熟悉的感觉。
本文提供了一个简明扼要且代码丰富的教程,带你进入JavaServer Faces。本文另外还:
- 介绍了JavaServer Faces。
- 描述了JavaServer Faces的优点。
- 介绍了JavaServer Faces的内部机制。
- 介绍了JavaServer Faces应用程序的起源。
- 带你体验JavaServer Faces应用程序的开发成果。
- 提供可以在应用程序中使用的示例代码。
请注意本教程没有演示如何开发自定义组件。
JavaServer Faces概览
JavaServer Faces是Java Community Process (JCP) 在Sun Microsystems的领导下开发的一种技术,在JCP中编号为JSR 127。它的目标是为Web应用程序的用户界面创建一个标准框架。前面提到,JavaServer Faces让你可以建立运行在Java服务器上的Web应用程序,并将用户界面呈现给客户端。这种技术通过一个控制器Servlet提供Web应用程序生命周期管理,并提供具有事件处理和组件呈现的丰富组件模型。
JavaServer Faces技术有两个主要组成部分:
- 用于表示UI组件,管理状态,处理事件,验证输入的Java API。这组API还支持国际化和可访问性。
- JSP自定义标签库,用于在JSP页面中表达JavaServer Faces。页面设计者可以使用这个标签库将UI组件加入到页面中。
图1显示了客户端,服务器,以及JavaServer Faces之间的关系。
图1:运行在服务器上的UI
在这里,JSP页面表示使用JavaServer Faces自定义标签库的用户界面组件,而不是将它们硬编码进标记语言中。应用程序的UI管理着JSP页面呈现的对象。
下面几种用户可以受益于这种技术:
- 使用标记语言如HTML的页面设计人员,将使用JSP标签库来表示JavaServer Faces中丰富的用户界面组件。
- 编写模型对象和事件处理程序的应用程序开发人员。
- 组件开发人员,用于创建基于JavaServer Faces组件的自定义组件。
- 在工具中提供JavaServer Faces支持的工具供应商,将JavaServer Faces技术集成在新一代工具中简化了多层的、基于Web的应用程序的开发过程。
这种技术还开辟了一个可重用Web用户界面组件的市场。开发人员和供应商可以使用JavaServer Faces作为开发客户界面的建立模块。
JavaServer Faces优点之一是它是基于模式-视图-控制器体系统结构(Model View Controller ,MVC)的,可以将表示和逻辑清晰地分离。正在使用现有Web框架如Struts的开发人员可能会对此感到熟悉。然而,注意JavaServer Faces和Structs不是互相竞争的技术,事实上,它们将会互相补充。不过,JavaServer Faces的确比Structs具有某些优势。例如,在Structs中只有一种呈现元素的方式,而JavaServer Faces提供了几种机制来呈现独立的元素。由页面设计者选择他们想要的表示方式,而应用程序开发人员不必知道使用哪种机制来呈现组件(从http://jakarta.apache.org/struts/proposals/struts-faces.html可以了解更多信息)。读者也许注意到Structs的设计者,Craig McClanahan也是制定JavaServer Faces规范的领导者之一,他也是Sun Microsystems的雇员。
JavaServer Faces应用程序的起源
JavaServer Faces应用程序就像任何其他基于Java技术的Web应用程序一样,它运行在一个Java Servlet容器中,包括:
- 包含特定于应用程序功能和数据的JavaBeans组件(或模型对象)。
- 事件监听器。
- JSP页面。
- 服务器端帮助器类。
- 用于表示UI组件的自定义标签库。
- 用于表示事件处理程序和有效性验证程序的自定义标签库。
- UI组件在服务器上表示为有状态的对象。
- 事件处理程序、有效性验证程序、以及导航处理程序。(有效性验证程序用于在更新服务端数据之前,验证各个组件中的数据的有效性。)
JavaServer Faces参考实现中包含一个组件标签库,html_basic。然而,高级开发人员可以开发他们自己的标签库,以便呈现自定义的组件。
JavaServer Faces参考实现提供了一个自定义的标签库,用于在HTML中呈现组件。下面是一个使用这个标签库的简单例子。如果想得到它支持的组件标签清单,请参考JavaServer Faces的 规范 和 教程。
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<body bgcolor="white">
<h2>What is your name?</h2>
<f:use_faces>
<h:form id="helloForm" formName="helloForm" >
<h:input_text id="username" />
<h:command_button id="submit" label="Submit" commandName="submit" />
</h:form>
</f:use_faces>
|
JavaServer Faces组件的体系结构是这样设计的:组件的功能由组件类定义,组件的呈现由一个单独的呈现器(renderer)定义。一个呈现器工具定义了组件类如何映射为适合特定客户的组件标签。JavaServer Faces参考实现中包含一个标准的RenderKit,用于将组件类呈现给HTML客户。HTML RenderKit中的每个JSP组件的组件功能由UIComponent类定义,组件的呈现属性由Renderer类定义。例如,标签command_button和command_hyperlink都表示一个UIComponent,但是它们是用不同的方式呈现的。Button组件呈现为一个按钮,而hyperlink呈现为一个超链接。
JavaServer Faces初步
要开始试用JavaServer Faces,需要安装Java Web Services Developer Pack (Java WSDP 1.0_01)或者Tomcat4.0+。 JavaServer Faces参考实现Early Access release (EA3)可以从以下网址下载: http://java.sun.com/j2ee/javaserverfaces/download.html。解压下载的文档后,在Windows下将得到下面的目录结构: c:\jsf-ea3>
example
lib
some-other-files
example目录包含示例应用程序的WAR和源文件。lib目录包含JavaServer Faces所依赖的JAR文件,这些文件是:
- commons-beanutils.jar:定义和访问JavaBeans组件属性的实用工具。
- commons-collections.jar:J2SE集合框架的扩展。
- commons-digester.jar:用于处理XML文档。
- commons-logging.jar:一个通用的、灵活的日志工具,允许开发人员用日志语句来调试(instrument)他们的代码。
- jsf-api.jar:包含javax.faces.* API类。
- jsf-ri.jar:包含JavaServer Faces Reference Implementation的实现类。
- jstl.jar:包含JavaServer Faces Standard Tag Library classes(JSTL)。
- jstl_el.jar:包含处理JSTL表达式语句的类。
- standard.jar:使用JSTL时需要用到它。
请记住JavaServer Faces参考实现是一个早期访问版本(EA),而且仅仅是JavaServer Faces规范的一个快照。换句话说,这个版本是不成熟的。还要注意当前的JavaServer Faces参考实现(RI)无法与Java WSDP 1.1协同工作。如果试图将它和Java WSDP 1.1一起使用,将会得到图2所示的异常:
图2:JavaServer Faces RI EA无法与Java WSDP 1.1协同工作。(点击放大)
如果没有Java WSDP 1.0_01,并且想实验JavaServer Faces RI EA3,我推荐使用Tomcat。在本文中,我使用Tomcat-4.1.24。一旦安装好Tomcat,启动它并在浏览器中输入http://localhost:8080以测试它(是否工作正常)。你应该能看到Tomcat的缺省页面。现在,要实验某个JavaServer Faces示例应用程序,只需将它们的WAR文件(从c:\path-to-JSF-installation)复制到Tomcat安装目录下的webapps中。要运行应用程序,在浏览器中输入http://localhost:8080/demo-name。例如,我将cardemo.war复制到Tomcat的webapps目录中,并在我的浏览器中输入http://localhost:8080/cardemo。图3是我得到的页面的一幅快照。
图3:JavaServer Faces cardemo示例应用程序。(点击放大)
创建自己的应用程序
这一节将介绍创建自己的应用程序的步骤。我在这里使用的例子是一个简单的窗体,它要求用户输入他或她的名字,然后单击Submit 按钮。应用程序将向用户显示一条问候消息。
- 创建下面的目录结构:
c:\tomcat4.1\webapps
hello
src (for Java files)
web (for web files: index.html and JSP pages...)
WEB-INF
web.xml
lib (JSF JAR files)
classes
|
这个结构主要说明我将创建一个名为hello的新应用程序。在hello子目录下,我创建了src(我在这里存放所有的Java源文件)和web(这里有WEB-INF子目录,它包含web.xml,以及lib子目录和classes子目录)。
- 从c:\jsf-ea3\lib中将所有的JAR文件复制到上面创建的lib子目录中。
- 创建一个web.xml,它用来配置Web应用程序。在JavaServer Faces中,它必须指定某些配置,如:(1)servlet上下文监听器,(2)用来处理在JavaServer Faces请求的servlet,以及(3)处理servlet的servlet映射。代码示例1显示了一个web.xml文件的例子,它定义了本例中的JavaServer Faces所需的配置。
代码示例1: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>
<listener>
<!-- Used to initialize and destroy the application helper and register
a Renderer to a RenderKit -->
<listener-class>BasicServletContextListener</listener-class>
</listener>
<!-- Faces Servlet -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<!-- FaceServlet should be loaded when the application starts up -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Faces Servlet Mapping. Map the path to the servlet when a request for
the servlet is received -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
</web-app>
|
- 创建带有JavaServer Faces标签的HTML文件。
首先我将编写index.html页面。当用户进入应用程序时,他们得到的就是这个页面,它允许用户单击应用程序启动它。如代码示例2所示:
代码示例2:index.html
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.75 [en] (WinNT; U) [Netscape]">
<title>index</title>
</head>
<body>
<P>Click <a href="../../../../developer/technicalArticles/GUI/JavaServerFaces/faces/index.html">here</a> to start the application.</P>
<br>
<hr WIDTH="100%">
</body>
</html>
|
当用户单击"here",就会载入index.jsp页面,如代码示例3所示:
代码示例3:index.jsp
<HTML>
<HEAD> <title>Hello</title> </HEAD>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<body bgcolor="white">
<h2>What is your name?</h2>
<jsp:useBean id="UserNameBean" class="UserNameBean" scope="session" />
<f:use_faces>
<h:form id="helloForm" formName="helloForm" >
<h:input_text id="username"
modelReference="UserNameBean.userName"/>
<h:command_button id="submit" label="Submit" commandName="submit" />
</h:form>
</f:use_faces>
</HTML>
|
这个JSP页面使用了一些重要的特性:
- 自定义标签库。组件标签库免去了在HTML中硬编码UI组件,结果得到的是可重用的组件。而且核心标签库使得注册组件的事件和其他操作变得很容易。
- <jsp:useBean>标签用来例示一个名为UserNameBean的JavaBeans组件,这个组件接下来在服务器上实现。
- form标签用来表示一个输入表单,这个标签用来表示表单组件,form标签中嵌套了input_text和command_button。
- input_text标签表示一个文本域,用户在其中输入一个字符串。这个标签具有两个属性:id和modelReference。id与组件对应,它是可选的。modelReference引用模型对象属性,它保存着输入到文本域中的数据。
- command_button表示一个按钮,它用来提交输入到文本域中的数据。
- 如果需要的话,编写模型对象(JavaBean组件)。
模型对象bean和所有其他JavaBean组件一样,它拥有一组访问器方法。代码示例4展示了一个JavaBean组件的例子,代码示例3中的index.jsp页面中引用了它。
代码示例4:UserNameBean.java
public class UserNameBean {
String userName = null;
public UserNameBean () {
}
public void setUserName(String user_name) {
userName = user_name;
}
public String getUserName() {
return userName;
}
}
|
- 处理事件。
下一步是为组件事件(如选中一个复选框或单击一个按钮以提交表单)编写一个事件处理程序类。对于简单的应用程序,需要定义当表单提交或访问链接时应该访问哪一个页面。可以通过实现ApplicationHandler接口来完成。代码示例5显示了一个例子。在这里,程序检查FormEvent事件是否是由index.jsp页面中的Submit按钮产生。如果是,组件树的ID被设置为与index.jsp页面相关联的组件树。
代码示例5:BasicApplicationHandler.java
import java.util.SortedMap;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.tree.Tree;
import javax.faces.tree.TreeFactory;
import javax.faces.FactoryFinder;
import javax.faces.lifecycle.ApplicationHandler;
import javax.faces.event.FormEvent;
import javax.faces.event.FacesEvent;
import javax.faces.event.CommandEvent;
import com.sun.faces.RIConstants;
public class BasicApplicationHandler implements ApplicationHandler{
public boolean processEvent(FacesContext context, FacesEvent facesEvent) {
if (!(facesEvent instanceof FormEvent) &&
!(facesEvent instanceof CommandEvent)) {
return true;
}
boolean returnValue = false;
String treeId = null;
if (facesEvent instanceof FormEvent) {
FormEvent formEvent = (FormEvent) facesEvent;
if (formEvent.getCommandName().equals("submit")) {
treeId = "/hello.jsp";
}
returnValue = true;
} else if (facesEvent instanceof CommandEvent) {
CommandEvent commandEvent = (CommandEvent)facesEvent;
UIComponent c = commandEvent.getComponent();
if (c.getAttribute("target") != null) {
treeId = (String)c.getAttribute("target");
returnValue = true;
}
}
if (null != treeId) {
TreeFactory treeFactory = (TreeFactory)
FactoryFinder.getFactory(FactoryFinder.TREE_FACTORY);
context.setTree(treeFactory.getTree(context,treeId));
}
return returnValue;
}
}
|
- 编写上下文监听器。
如果仔细观察部署描述符文件web.xml,将会发现我声明了一个servlet上下文监听器(BasicServletContextListener)。servlet容器在应用程序启动时调用监听器的contextInitialized方法,创建一个servlet上下文监听器;当应用程序关闭时,调用监听器的contextDestroyed方法。代码示例6展示了一个上下文监听器的例子。
代码示例6:BasicServletContextListener.java
import javax.servlet.ServletContextListener;
import javax.servlet.ServletContextEvent;
import javax.faces.FactoryFinder;
import javax.faces.lifecycle.LifecycleFactory;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.ApplicationHandler;
public class BasicServletContextListener implements ServletContextListener {
public BasicServletContextListener() {
}
public void contextInitialized(ServletContextEvent e) {
ApplicationHandler handler = new BasicApplicationHandler();
LifecycleFactory factory = (LifecycleFactory)
FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
Lifecycle lifecycle =
factory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
lifecycle.setApplicationHandler(handler);
}
public void contextDestroyed(ServletContextEvent e){
}
}
|
- 编写一个响应页面。
当index.jsp中的表单提交之后,应用处理程序被调用,用户进入到响应页面,hello.jsp,如代码示例7所示:
代码示例7:hello.jsp
<HTML>
<HEAD> <title>Hello</title> </HEAD>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<body bgcolor="white">
<f:use_faces>
<h:form id="responseform" formName="responseform">
<h2>Hello, <h:output_text id="userLabel"
modelReference="UserNameBean.userName" /> </h2>
<p>
</h:form>
</f:use_faces>
</HTML>
|
- 编译和运行应用程序。最后一步是编译Java类和运行应用程序。在Web浏览器中输入URL:http://localhost:8080/hello,将得到如图4所示的index.html。
图4:进入应用程序
当单击"here"超链接,将会得到类似于图5所示的页面。
图5:开始应用程序
现在,输入你的名字并单击Submit按钮,将得到类似于图6所示的页面。
图6:正确运行的应用程序
输入验证
JavaServer Faces提供了一组标准的内置验证机制(或称验证程序),通过实现Validator 接口及其validate方法,允许开发人员创建自定义的验证程序。这五个内置的验证程序是:
- DoubleRangeValidator:检查组件的值是否在某个范围之内。值必须是一个浮点数,或者可以转换为一个浮点数。通过validate_doublerange标签可以使用这个验证程序。下面是一个例子:
<input_number id="less" formatpattern="#.## size="5">
<validate_doublerange minimum="1.0" maximum="3.14"/>
</input_number>
- LengthValidator:检查值(必须是javalang.String类型)的长度是否在某个特定的范围之内,通过validate_length标签使用这个验证程序。下面是一个例子:
<input_text id="creditCardNum" size="16">
<validate_length minimum="16" maximum="16"/>
</input_text>
- LongRangeValidator:检查值(任何可以转化为long的类型)的长度是否在某个特定的范围之内,通过validate_ longrange标签使用这个验证程序。下面是一个例子:
<h:input_number id="zip" formatpattern="#####" size="5">
<validate_longrange minimum="50000" maximum="10000"/>
</input_number>
- RequiredValidator:检查组件的值是否为null,如果值的类型是String,它确保组件的值不是一个空串,通过validate_ required标签使用这个验证程序。下面是一个例子:
<input_text id="creditCardNum" size="16">
<validate_required/>
<validate_length minimum="16" maximum="16"/>
</input_text>
- StringRangeValidator:检查组件的值(必须是javalang.String类型)是否在某个特定的范围之内,通过validate_ stringrange标签使用这个验证程序。下面是一个例子:
<input_text id="middleInitial" size="1">
<validate_stringrange minimum="A" maximum="Z"/>
</input_text>
结论
JavaServer Faces是一个用户界面框架,用于构建运行在服务器端的Web应用程序,并将用户界面返回给客户端。使用它可以开发一些工具,简化基于Web的Java应用程序编程。Sun和JavaServer Faces专家组的其他成员,包括Borland,IBM,Macromedia 以及Oracle,以及许多其他公司和个人,正在评估将JavaServer Faces技术结合进新一代工具中的途径,这些工具将简化基于Web的多层应用程序的开发。
JavaServer Faces控件提供了广泛的用户交互方式,使用基于JavaServer Faces的应用程序的用户会喜欢这些。与标准的HTML前端相比,它可以提供更多的特性,使用更加方便。记住,与普通的JSP配置相比,使用JavaServer Faces需要做更少,而得到的益处却多得多。 |