Summary In September 2002, the early access (EA) draft of the JavaServer Faces specification was released under Java Specification Request (JSR) 127. JavaServer Faces, with a well-defined request processing lifecycle and a rich component hierarchy, will profoundly affect the development of Java 2 Platform, Enterprise Edition (J2EE) applications. In Part 1 of this two-part series, David Geary introduces JavaServer Faces and explores its fundamental concepts. (3,000 words; November 29, 2002)
摘要:在2002年9月的127次JSR会议上发表了JavaServer Faces规范的早期草稿版本。具有良好定义的请求处理生命周期和丰富的组件层次结构的JSR将会深远的影响到J2EE应用的开发。在本篇分为两部分的文稿的第一部分中,David Geary将介绍JSR并初步探讨它的基本概念。
Recently, I had the good fortune of training and mentoring a group of novice Java developers as we implemented a complex Web application using Struts, Enterprise JavaBeans (EJB), servlets, JavaServer Pages (JSP), and the JSP Standard Tag Library (JSTL). As it turned out, the project was a success; it came in under budget and on time, and had numerous features not originally envisioned. As you might imagine, we faced many technical challenges along the way; the most significant were:
最近,我在使用Struts、EJB、JSP和JSTL开发一个复杂的Web应用的过程中能有幸培训和指导一组共同参与开发的初级Java开发人员。当开发完成以后,整个项目非常成功并且是在经费预算和进度规划内完成的,而且实现了很多原先没有设想到的功能特性。当然我们在开发的过程中也遇到了相当多的技术挑战,最典型的有:
1. Implementing custom components, Which included a tree/table viewer and a query builder that lets users dynamically add and remove fundamental components such as text fields and drop-down lists used to build database queries.
1. 实现客户端组件,诸如树型、列表的视图和允许用户在创建数据库查询时动态的增加和删除类似于文本输入框或者下拉列表等基本元素的查询构造器的客户端组件。
2. Supporting hand-held devices, such as PDAs and radio frequency devices.
2. 支持手持设备,例如PDA和射频设备。
3. Lack of an IDE for effective rapid application development (RAD).
3. 缺乏有效的能够支持快速应用开发的集成环境。
Implementing custom components and supporting hand-held devices—especially the latter—consumed a great deal of our time and effort. Also, although some of the developers used the Eclipse open source IDE, we lacked an effective RAD tool for implementing the Web application's user interface.
实现客户端组件和支持手持设备消耗了大量的时间精力。尽管一部分开发人员使用了开 放源码的Eclipse基层开发环境,但是我们在Web应用的客户界面开发方面还是没有有效的RAD工具。
Unless you've been living in a cave for the past few years, I'm sure you're aware that tools exist for creating custom Web components and supporting markup languages other than HTML, all of which are wrapped up in a very nice IDE. That software, of course, is Microsoft's .Net with WebForms; the IDE is Visual Studio.
我确信除非你不韵世事,否则你肯定知道用于开发用户Web组件和支持HTML以外的标记语言的工具已经被集成到了一个非常不错的开发工具中了。当然这个工具就是Microsoft's .Net with WebForms,而这个开发环境就是Visual Studio。
In spite of those attractive .Net features, the company I was working
for—like many software development companies nowadays—opted to go with the Java 2 Platform, Enterprise Edition (J2EE) because of its platform and
vendor independence and the wealth of available open source software for Java and J2EE.
尽管.NET具有很多引人瞩目的特性,我所工作的公司和目前的许多软件开发公司一样,因为J2EE的平台无关性和大量可以利用的开放源码的软件而倾向于在J2EE上进行开发
Wouldn't it be nice if you could take advantage of Java and .Net's best features, platform and vendor independence, open source products such as Ant and log4j, and the ability to easily create custom Web components and render them to multiple devices, all wrapped up in a killer IDE? That's the promise of JavaServer Faces.
如果我们能把所有这些Java和.NET各自的优点,平台无关的特性,类似Ant和Log4j这样优秀的开放源码的软件产品以及能够简便的生成客户Web组件并发布到多种设备上的能力组合到一个独一无二的集成开发环境中,那这个开发环境将是非常的优秀啊?这就是 JSF将提供给我们的东西。
JSF是什么?
JavaServer Faces (JSF) is an application framework for creating Web-based user interfaces. If you are familiar with Struts (a popular open source JSP-based Web application framework) and Swing (the standard Java user interface framework for desktop applications), think of JavaServer Faces as a combination of those two frameworks. Like Struts, JSF provides Web application lifecycle management through a controller servlet; and like Swing, JSF provides a rich component model complete with event handling and component rendering.
JSR是生成基于Web的用户界面的应用程序框架。如果你对Struts(流行的开放源码的Web应用程序框架)和Swing(标准的用于桌面应用的Java用户界面框架)都很熟悉的话,可以认为JSF是他们二者的集成。类似于Struts,JSF通过一个控制器Servlet提供了Web应用的生命周期管理;同时类似于Swing,JSF提供了包括事件处理和组件生成在内的丰富的组件模型。
In a nutshell, JSF eases Web-based application development because it:
Lets you create user interfaces from a set of standard, reusable server-side components Provides a set of JSP tags to access those components Transparently saves state information and repopulates forms when they redisplay Provides a framework for implementing custom components Encapsulates event handling and component rendering so you can use standard JSF components or custom components to support markup languages other than HTML Lets tool vendors develop IDEs for a standard Web application framework
简单的说,JSF基于以下的原因简化了基于Web的应用的开发:
◆使你能够利用一些标准的可重用的服务器端组件来创建客户界面。
◆提供了一组JSP 标签来获取(访问)这些组件。
◆开发人员不用关心当页面刷新的时候页面状态数据的存储和重现。
◆提供了一个用于实现定制组件的框架
◆封装了事件处理和组件显示,所以你可以使用标准的或者定制的JSF组件支持HTML以外的标记语言。
◆开发工具提供商可以开发针对标准Web应用框架的集成环境。
Besides being a conceptual combination of Struts and Swing, JSF is a direct competitor to Microsoft's WebForms. The frameworks are very similar, both in concept and implementation. And because JSF represents a standard for Java-based Web application frameworks, tool vendors can concentrate on developing IDEs for JSF instead of developing an IDE for one of approximately 35 existing Java-based Web application frameworks, including Struts.
除了作为Struts和Swing概念上的统一体外,JSF还将成为Microsoft的WebForms的直接 竞争对手。这两种框架不论从概念或者实现上都非常相似。由于JSF作为基于Java的Web 应用框架的标准,工具开发商们可以专注于为JSF开发集成环境,而不仅仅为包括Struts在内的大约35种基于Java的Web应用框架的其中之一开发集成环境。
Note: Struts developers needn't worry; although JSF and Struts have much in common, JSF will not make Struts obsolete. See Resources for a discussion of an integration strategy for Struts and JavaServer Faces.
注:Struts开发人员不用担忧;尽管JSF和Struts很相像,但JSF并不会放弃Struts。在参考资料中有关于集成两者的讨论。
Currently, JSF is an early access (EA) release, and, as a result, is somewhat immature. The specification leaves some functionality unspecified, and the specification and reference implementation are currently out of sync, with the former specifying new syntaxes and functionality not yet implemented in the latter. On the other hand, JSF is mature enough for you to write code against—although much of that code is guaranteed to be obsolete (see the disclaimer below)—and the reference implementation is fairly complete and relatively bug-free. You can download the JSF specification, the reference implementation, two sample applications, and a JSF tutorial from Resources.
目前JSF还仅仅是EA版本,所以不是很成熟。规范当中还有很多功能没有阐述,规范和 参考实现也不同步,规范中描述的语法和功能还没有得到实现。另一方面,JSF已经足够成熟得让你根据它进行编程——尽管有些代码将会被废弃(参看下文声明)——而且参考实现已经基本完成而且比较可靠。你可以从参考资源中下载JSF的规范、参考实现、两个样例应用和一份JSF教程。
The two articles in this series provide a code-intensive introduction to JavaServer Faces. In this article, I begin with a short discussion of the JSF lifecycle and then dive into some example code that illustrates implementation of Web-based user interfaces with JSF and how you can take advantage of built-in validation. In Part 2, I will explain more advanced JSF concepts such as: implementing custom validation; using model objects; internationalization; creating custom components; and finally, delegating event handling and rendering so you can use components to generate markup languages other than HTML.
本系列的两篇文章提供的是并不偏重编码的JSF介绍。在本篇中,我在简短的介绍了JSF的生命周期以后将通过一些示例代码来说明如何通过JSF来实现基于Web的用户界面以及如何利用内建的验证功能。下一篇中,我将阐述一些JSF的高级概念:实现定制验证、使用模型对象、国际化支持、生成定制组件和便于你使用组件生成HTML以外的标记语言的事件的代理和处理机制。
Disclaimer: The code discussed in this article was written against the EA2 JSF reference implementation. As mentioned above, the specification and reference implementation are in a state of flux, and therefore, the code in this article is guaranteed to be obsolete in the near future; however, the code works as advertised with the EA2 reference implementation and was tested with both Tomcat 4.0.6 (the latest production release of Tomcat) and Resin 2.1.6. Furthermore, you can read the JSF specification until the cows come home, but to really grasp the concepts, you must ruminate over some code.
声明:本文中的代码均针对EA2版本的JSF参考实现编写。前面已经说明,规范和参考实现目前还在修订当中,这些代码将很快会被废弃;然而这些代码可以在EA2版本的参考实现上运行,并且在Tomcat 4.0.6和Resin 2.1.6上通过了测试。当然你也可以研读规范直至领会,但是如果你想真正掌握概念,就应该做一些深入的编码实践。
JSF的运行周期(The JavaServer Faces lifecycle)
JSF handles HTTP requests with seven distinct phases, as shown in Figure 1. The normal flow of control is shown with solid lines, whereas dashed lines show alternate flows depending on whether a component requests a page redisplay or validation or conversion errors occur.
JSF通过7个步骤来处理一个HTTP请求,如图1。正常的处理流程通过实线标识,虚线表示一些诸如刷新显示、验证错误、转换错误等特殊情况的可选的处理流程。
The Reconstitute Request Tree phase creates a component tree for the requested page. If that page previously displayed and JSF saved the page's state information, the state information is added to the request. This means that JSF automatically retains form information when a form redisplays; for example, when a user does not correctly fill out a form. This handy feature is a fundamental capability provided by Microsoft's WebForms, but is otherwise absent from J2EE.
重建请求树阶段为被请求的页面创建组件树。如果这个页面曾经被访问过,JSF会保存 上次访问时的状态信息和数据,在此时将这些状态数据加入到当前的请求。这意味着当重复访问某个表单的时候JSF将自动的恢复表单数据。这个便利的功能在Microsoft的WebForms中是基本功能,但是在J2EE中则被遗漏了。
During the Apply Request Values phase, the JSF implementation iterates over the components in the component tree and calls each component's decode() method. That method extracts information from the request and stores it in the component. Optionally, components may delegate decoding to a renderer.
在请求数据解析阶段中,JSF实现遍历组件树中所有的组件调用所有组件的decode()方 法。decode()方法将从请求中提取信息并存储到组件当中。某些情况下,组件可用于为响应合成器提取数据。
In addition to decoding request information during the Apply Request Values phase, components or their renderers may create request events. Typically, request events signal a visual change for one or more components; for example, clicking on a graphic in a tree control may expand a branch of the tree. Alternatively, an event in one component may update the visual representation of another component; for example, clicking on a leaf node in a tree may cause an associated list to change its contents and redisplay. In either situation, a request event is generated and added to the JSF context.
另外在请求数据解析阶段提取请求信息的过程中,组件或者他们的响应合成器可能会生成一些请求事件。最典型的有请求事件可能会触发了一个或多个组件的显示变化;例如当点击一个树型控件可能会展开它的分支。另外,某个组件的事件可能会更新另一个组件的显示;例如当点击一个树型控件的叶节点可能会导致和它关联的列表控件改变它的内容和显示。在任何一种情况下,都会产生一个请求事件加入到JSF的运行环境中。
Request events, which are generated during the Apply Request Values phase, are handled during the Handle Request Events phase. During the Handle Request Events phase, the JSF implementation calls the processEvents() method for each component that has one or more request events. Components may handle request events themselves, or they may choose to delegate event handling to an event handler. The processEvents() method is a boolean() method. If that method returns false, lifecycle processing advances to the Process Validations phase; otherwise, lifecycle processing advances directly to the Render Response phase.
请求数据解析阶段产生的请求事件都在请求事件处理阶段处理。在请求事件处理阶段JSF实现为那些有一个或多个请求事件的组件调用processEvents()方法。这些组件将自行处理这些事件或者提交给某个事件处理器代理处理。processEvents()方法返回值为boolean。如果返回false,处理周期将前进到验证处理阶段,否则将直接跳转至响应合成阶段。
During the Reconstitute Request Tree phase, the JSF implementation may register one or more validators for any of the components in the component tree. In the Process Validations phase, the JSF implementation invokes the validate() method for each validator. Validators perform correctness checks and return a boolean value from their validate() method; if that method returns true, the JSF lifecycle proceeds normally; otherwise, the JSF implementation invokes the Render Response phase directly.
在重建请求树阶段,JSF实现会为请求树上的组件注册一个或多个验证器。在验证处理阶段,JSF实现调用每一个验证器的validate()方法。验证器执行validate()方法进行正确性检验以后返回一个boolean值;如果为true,JSF处理周期将继续前进;否则将直接跳转至响应合成阶段。
Each JSF user interface component can be associated with a field in a Java object (known as a model object). During the Update Model phase, component values are copied to the component's model object. A component's updateModel() method carries out that data transfer. Conversion errors can occur during this phase because request parameters are strings, but model values can represent any type of Java object. If a conversion error occurs, the JSF implementation invokes the Render Response phase directly.
每一个JSF用户界面组件都关联于一个Java对象(被称为模型对象)的某个字段。在模型更新阶段,组件的值将被复制到对应的组件当中。数据传递通过模型对象的updateModel()方法调用完成。由于请求参数都是字符串而模型对象字段有可能是任何Java对象类型,如果发生转换错误,JSF实现将直接跳转至响应合成阶段。
In a JSF application, if you submit a form or click on a link (both of which must be represented by JSF components), the JSF implementation creates a form event or a command event, respectively. Those events are handled in the Invoke Application phase by an application-specific handler. Typically, those handlers specify a URL, and the JSF implementation forwards the request to that URL. Currently, application-specific handlers handle form and command events in a single method, typically with a switch statement. The JSF expert group is aware of this approach's ugliness, and therefore, it's almost certain to change in the JavaServer Faces 1.0 release.
在JSF应用中,如果你通过提交一个表单或者点击一个连接(都需要是通过JSF组件提供的),JSF实现将分别生成一个表单事件和命令事件。这些事件将在调用Web应用阶段由一个应用声明的处理器进行处理。典型的情况是处理器声明了一个URL,JSF实现将这个请求前转到这个URL中。目前应用声明的处理器对此的处理都是通过一个包含一个switch语句的单一方法进行的。JSF专家组意识到这样的方案不是很合理,因此在JSF 1.0的正式发行版将肯定会有所改变。
Finally, the Render Response phase creates a response component tree and forwards the response. When a user submits a form, clicks on a link, or otherwise generates a request, the cycle starts anew.
最后,响应合成阶段生成一个响应组件树并发送响应。当用户再次提交一个表单或点击一个链接,总之生成一个请求,那么处理流程将重新开始。
Now that we have a general overview of JavaServer Faces and a rudimentary understanding of the JSF lifecycle, let's take a look at some code.
现在我们已经对JSF和处理流程有了一个粗略的认识,下面将研究一些代码。
Seegeris,Bruce