jsf简介

来源:互联网 发布:python常用函数 编辑:程序博客网 时间:2024/05/16 00:26

2.1  关键部件

同大多数技术一样,Faces拥有一套术语,这些术语构成了它所提供的各种特性的概念基础,包括用户界面(UI)组件、验证器(validator)和呈现器(renderer)等。你可能已经很清楚它们是什么了,但为了编写Faces应用,则必须理解在JSF领域中它们指的是什么。在下一节,我们将涉及这些关键概念,并解释它们之间的关系。

在开发JSF应用前,先熟悉8个基本术语(见表2-1)。

表2-1  JSF关键部件的术语

术    语

说    明

UI 组件(也称控件或简称组件)

有状态的对象,在服务器中维护,提供与用户的特定交互功能。UI 组件是具有属性、方法和事件的JavaBean。它们被组织进一个视图,而该视图通常显示为一个页面的组件树

呈现器(renderer)

负责显示UI组件并且将用户输入转换为组件的值。呈现器可以设计来与一个或者多个UI 组件一起工作,而一个UI 组件可以和多个不同的呈现器相关联

验证器(validator)

负责检查用户输入以确保输入的值是可以接受的。一个UI组件可以和一个或者多个验证器相关联

后台bean(backing bean)

特殊的JavaBean,从UI组件收集用户输入值并且实现事件监听器方法。它们也可以保存UI组件的引用

转换器(converter)

在组件的值和供显示的字符串之间进行转换。一个UI组件可以和一个转换器相关联

(续)

术    语

说    明

事件和监听器(event 和listener)

JSF使用JavaBean事件/监听器模型(也用于Swing中)。UI 组件(以及其他对象)可以产生事件,然后注册监听器来处理这些事件

消息(message)

回显给用户的信息。只有某些应用部件(后台bean、验证器、转换器,等等)可以产生显示给用户的消息和错误信息

导航(navigation)

从一个页面转移到另一个页面的能力。JSF 具有一个集成到特殊的事件监听器中的强大的导航系统

现在我们来看看这些术语之间的关系。请看图2-1,这是一个UML类图,以简化的关系表示了上面所述的每个概念(为了完整还包括了视图(view)和模型对象(model object)作为附加的概念)。如图所示,包含在视图中的UI 组件更新后台bean,并且根据用户的输入产生事件。呈现器显示组件,也可以产生事件和消息。转换器转换并格式化组件的值以供显示,也产生错误信息。验证器验证组件的值并产生错误消息。

图2-1  表示JSF关键概念之间关系的模型,它们通过事件和消息进行通信

后台bean 包含事件监听器和特别用于导航的事件监听器的动作方法。事件监听器处理事件并且操纵视图或者执行模型对象,后者执行核心应用逻辑。动作方法可以实现事件监听器能实现的所有功能,同时它还返回一个用于导航系统的结果。最后,导航系统根据这一结果来选择显示给用户的下一个视图。

图中的大部分概念都会产生消息或者事件,这也是JSF应用进行通信的基础。事件表示用户输入和应用操作,消息指示错误或者应用提示。

你可能已经注意到,事件、消息和模型对象从JSF的角度来看都是被动的。换句话说,它们不做任何事情,而其他类总是操作它们。这对模型对象来说是很重要的,因为这意味着模型对象无需知道用户界面的任何信息。这也是JSF体现遵循MVC架构的原则之处。

现在,你应该已经对这些术语的含义及它们之间的关系,有了初步的印象。下面依次详细讨论它们。

2.1.1  UI组件

UI组件(也称控件,或者简称组件)主要用于与最终用户的交互。Visual Basic有UI组件,Swing也有,那么Faces 组件有何独特之处呢?像Swing 控件一样,它们构建在JavaBeans之上。这意味着它们有属性、方法和事件以及对IDE的固有支持。与Swing不同,它们特别针对独特的Web应用的约束做了一些特殊设计,并且它们是驻留在服务器端而不是客户端的。这一点很重要,因为大部分Web界面都不是构建在组件之上,而仅输出标记,比如HTML。

将UI组件打包成一个组件(如toolbar 或 calendar)使得开发更加容易,因为核心功能被封装到一个可重用的代码块中。例如,如果需要使用calendar控件,则不必为了正确显示而开发一套复杂的HTML、CSS和图片。你可能不得不操作一些属性,比如颜色或者默认日期,但是所有复杂的工作都已经被组件开发者完成了。现在不需要编写大量的代码使其和你的应用集成,只需要通过后台bean(后面讨论)的一个属性与之关联。

如果仔细考虑,一个calendar可能有多种不同的表达方式。最常见的方式是一次显示一个月,每一个小框显示该月中的每一天。calendar也可以显示为三个下拉列表框:分别表示日、月、年。组件的表示方式取决于其呈现的方式。

不管怎么呈现,calendar 控件都有相同的基本功能和一组固有的属性,比如颜色、默认日期以及希望显示的日期范围。calendar组件只表示calendar 做什么,而不表示看起来怎样。这些行为是呈现器无关的,因为不管组件如何呈现,它们都是一样的。在Java中开发组件时,这种差别是很关键的,但是对于前端或者应用开发人员来说,可以在根据特定客户环境裁减的界面中使用组件(例如,HtmlInputText 组件具有HTML特定的属性(如accessKey和style),但从技术上来说它们并不自我呈现)。

基于Web和基于桌面的组件之间的关键差别在于,前者并不直接和用户机器发生交互。如果在桌面应用中填错表单并且点击了OK,页面并不重新显示,程序只是报告哪里填错了(至少你希望它这么做!)。表单中的所有值不变。而在Web应用中,通常页面会被刷新,并显示相关的错误信息,但是应用不得不想法使它看起来未被重新显示一样。换句话说,组件必须记住自己的值,或者说是状态。JSF 组件将自动为你处理这些。

Faces组件可以在请求之间记住各自的值,因为框架为每一个给定页面维护了一个UI组件树。这个组件树,称为视图是JSF页面的内部表示,在其中存在父子关系。比如一个表单,它包含一个标签(label),一个文本字段,一个具有两个嵌套按钮的面板(panel),如图2-2。使用视图而不是页面强调了用户表示并不总是HTML Web页面。然而,为了简化起见,我们将交替使用它们。

组件树中的每一个组件都用组件标识符(component identifier)标识。组件标识符可以由开发人员设置,如果没有,JSF可以自动生成一个。另外,组件可以与其他组件通过命名关系如页眉或页脚,发生关联——这称为facet。UI也支持可访问性(accessibility)属性,使残障人士能够更容易地访问它们。

UI组件提供了一种抽象形式,为Web开发引入了全新的灵活性。使用JSF 开发UI,更多的是组装和配置组件,而不是编写大量让人厌烦的各种代码(HTML、CSS、JavaScript等)。并且所有这些组件都可以在GUI设计器中通过代码或者声明(通过某种显示技术,比如JSP)来引用和操作。Faces 包括多种标准的组件,比如标签、超链接、文本框、列表框、复选框、单选按钮、面板和数据网格等。我们将在第4章和第5章详细讨论它们,用户定制组件的开发将在第四部分和第五部分在线扩充中提及。

图2-2  UI组件是在服务器中的视图或组件树中进行管理。组件可以直接关联到JavaBean 属性的值。组件在客户端呈现为HTML(或者其他形式的显示语言)

2.1.2  呈现器

Faces UI组件一般并不负责自身的呈现。当组件自我呈现时,一般称为直接实现(direct implementation)模型,JSF也支持委托实现(delegated implementation)模型,这允许由其他类来处理呈现过程。令人惊讶的是,这些类称为呈现器

呈现器被组织进呈现包(render kit),它们通常关注于特定类型的输出。JSF 本身带有一个针对HTML 4.01的标准呈现包,但是一个呈现包可以产生不同的HTML 外观和感觉(look and feel,或称skin),无线标记语言(WML)、可伸缩矢量图形(SVG),或者它可以与其他applet、Java application或者完全不同类型的客户端进行通信。

你可以将呈现器想像成客户机和服务器之间的翻译。从服务器出发的角度看,它处理编码(encoding),即创建客户能够理解的组件的表示方式。当JSF从用户接收到一个请求时,呈现器将进行解码(decoding),即提取正确的请求参数并且基于这些参数设置组件的值。

例如,下面的代码定义了一个HtmlInputText组件:

当该组件被编码时,发送给用户的是下面的HTML片断:

假设用户在文本域中输入文本foo。解码动作将从HTTP请求中发送的表单参数中提取参数,然后将服务器中的HtmlTextInput 组件实例的值设置为foo。在幕后,标准HTML呈现包中的文本呈现器处理这两个流程。因为所有的编码和解码工作都由一个实体来实现(不管是组件还是呈现器),组件的可视化表示,以及在请求参数和服务器对象之间转换的基本协议,都是非常优雅地自包含的。

组件使用委托实现模型时(正如所有标准化组件所为),修改给定页面的整个显示只需简单地修改呈现包即可。这意味着可以容易地使用与Faces兼容的模板来显示HTML、WML、SVG——组件是一样的,显示为不同的标记语言只需要改变呈现包。

值得注意的是,对大多数HTML 应用开发任务,呈现器是很透明的。所有标准组件都与幕后的一个呈现器相联系,所以不需要过多地担心它。然而,如果需要动态改变应用外观,或者要开发定制组件,呈现器则是JSF组成中最基本和最强大的一部分。我们将在第四部分和第五部分在线扩展中讨论它。

2.1.3  验证器

开发UI的乐趣之一是,确保用户输入正确的内容——这可能同“数据库中正确的零件标识符”一样复杂,也可能简单至“不能为空”的规则。通常,执行这种保证需要在JavaScript和/或Java代码中使用大量丑陋的if语句。另外,如果没有框架帮你打理,显示错误消息也很容易出错。

Faces 以三种方式处理验证——在UI 组件层面,通过后台bean或者验证器类中的验证方法。UI组件通常处理简单的验证,比如值是否是必需的,或者针对组件本身的验证逻辑(因此不适于其他组件)。 验证器方法在需要验证表单中的一个或者多个字段时很有用(并不需要和其他组件共享该逻辑)。外部验证器在验证字段长度或数字范围时非常有用,它们是可插入的,这意味着可以为一个组件关联一个或者多个这种验证器。验证在服务器上进行,因为并不是所有客户端都支持脚本(JSF 组件也可以支持客户端验证,但是没有标准的组件来实现)。

当验证器遇到错误,比如字符串太长或者无效的信用卡号码,它便在当前的消息列表中添加一个错误消息。这使得使用标准JSF组件向用户回显错误消息变得非常容易。下面是一个例子:

你可以看到一个验证器与一个组件关联是多么容易。这里定义了一个HtmlInputText 组件,有一个长度验证器与其相联,以检查并确保用户输入的长度在2~10个字符。

一直以来,输入验证都是令人讨厌的Web 开发任务。验证器提供了强大的框架来简化这个任务。JSF附带了一套标准的验证器来进行诸如输入长度和范围检查之类的验证,但也可以自己编写,或者使用第三方的验证器。标准验证器将在第6章讨论,而定制验证器将在第20章讨论。

2.1.4  后台bean

第1章讨论了MVC设计模式,以及如何将应用划分成模型、视图和控制器层。我们提到,模型由应用逻辑和数据组成,视图由UI组成,而控制器则定义二者之间的交互。

在JSF应用中,扮演后一个角色(与UI和模型交互)的是后台bean。后台bean通常包含想要从用户处收集的属性,以及处理这些属性、操纵UI和执行其他一些应用处理的监听器方法(后面讨论)。一些开发工具会在创建新页面时自动产生后台bean类。也可以称之为Web窗体(web form)或者后台代码(code-behind),这强调此概念类似于ASP.NET 中的后台代码文件(但从根本上来说,它们完全不一样)。

JSF允许将UI组件声明式地与后台bean相联。通过声明,我们使用的是标记而不是代码(也可以在代码中使用)。还可以通过JSF表达式语言(EL)与后台bean相连,这种表达式语言类似于JSP 2.0和JSTL的表达式语言。你可以在应用的任何地方使用JSF EL表达式,来引用特定的后台bean属性。例如,Hello, world!示例中的这段代码:

这段代码将HtmlOutputText组件的值直接与helloBean对象的numControls属性相关联。当组件的值改变,helloBean.numControls属性也跟着改变。如果helloBean.numControls属性先改变也是如此。两者将自动保持同步。这是JSF的关键特征,也是将后台bean属性与UI组件的值相关联的基础。

你也可以直接关联或者绑定后台bean属性至服务器端的组件实例。这在想要通过Java代码操纵组件时很有帮助,而这有时是由事件监听器处理的流程。例如,Hello, world!应用有个HtmlPanelGrid组件实例绑定到HelloBean后台bean属性:

这里,组件的binding属性用一个JSF EL表达式将它与HelloBean的controlPanel属性相关联,而该组件是HtmlPanelGrid类型的一个实例。因为后台bean有个到实际组件的引用,于是可以在代码中这样操纵它:

这段代码位于HelloBean事件监听器方法中,获取controlPanel的子组件并将其全部删除。这个改变将在页面被重新显示时呈现给用户。

顺便
说说

               如果你过去总是认为JavaBean仅可用于值对象(仅有属性的对象),那么一定要记住它还有很多用处。后台bean是完全的JavaBean,包含有属性,也包含处理这些属性的事件监听器方法。它们可以直接与UI组件相关联,所以如果必要,你可以直接操作视图。

一个视图可以有一个或者多个后台bean,某些开发工具可以自动创建从UI组件到对应的后台bean属性的绑定。

后台bean通常和模型对象(辅助(helper)类),进行交互;辅助类访问数据库、Web服务以及EJB之类的服务,或者执行应用逻辑,或者表示诸如用户、用户偏好、报表、交易,等等。同后台bean一样,模型对象也可直接使用JSF表达式直接与组件的值相关联。例如,想要将输入控件与User对象的name属性相关联。然而,因为不知道任何有关UI的信息,模型对象不能直接绑定至UI组件。

这样应用开发人员无需花费大量的时间来编写创建后台bean和模型对象的代码,JSF 提供可声明机制来创建它们,称之为受管bean创建工具(managed bean creation facility)。它允许指定哪一个对象可以在整个应用的哪个生命周期内有效。下面是一段Hello, world!的代码片段:

这段代码告诉JSF 创建一个HelloBean类的实例,并命名为helloBean,然后将它保存在用户的会话中。这小段代码就是使对象有效地与UI组件集成所需的全部代码。任何使用这种方式的对象都称为受管bean。

理解JSF如何同后台bean以及模型对象交互是构建Faces应用的基础之一。框架的主要目标之一是简化集成UI和模型的负担,你越是使用它,越会发现更多能使目标变成现实的特征。

你在整本书中都会看到对后台bean 的引用,同时我们将在第13章开发一些后台bean。

2.1.5  转换器

用户与JSF应用交互时,它们其实是与呈现器的输出进行交互,即呈现器针对特定的客户端(比如浏览器)创建的特定的表现。为实现这一功能,呈现器必须知道一些关于所显示的组件的特定信息。但是组件也可以与后台bean属性相关联。这些属性可以是表示名称的字符串、表示生日的日期、或者表示foo属性的FooBarBazz,等等。因为这里对类型没有约束,呈现器预先并不知道如何显示这些对象。

这时该转换器出场了。它们将对象转换成字符串以供显示,或者将输入字符串转换成对象所需的格式。一个转换器可以和任何控件相关联。JSF 附带了一些通用的转换器,比如日期和数值,但是你或者第三方也可以自己开发其他转换器。呈现器(或者组件自身)通常会在编码和解码时在内部使用转换器。

转换器也处理格式化和本地化。例如,DateTime转换器可以将Date对象格式化成短(short)、中(medium)、长(long)或者完整(full)格式。对某些给定格式,将根据用户场所来决定日期的具体显示方式。下面是注册转换器到HtmlOutputText 组件中的代码:

假定用户的生日是1942年5月4日。如果用户在美国,HtmlOutputText组件将显示“05/04/42”;如果他在加拿大,则显示“04/05/42”。如图2-3所示。

图2-3  转换器将对象转换为可供显示的字符串形式,它也能处理格式化且支持不同语言

转换器用起来很顺手,因为它们执行了必要的功能——将对象转换为可显示的字符串,它还提供了其他一些有用的特征。它们使格式化和本地化通用的数据类型更容易,并且这种架构还允许开发人员对其模型对象做同样的工作。标准的转换器将在第6章介绍,而转换器的开发将在第15章讨论。

2.1.6  事件和监听器

我与妻子养了几只猫。它们都要吃不同的食品,所以我每天喂它们两次而没有使用投食器。有趣的是,每天早上我下楼或者晚上回到家,它们都要向我乞食。这听来是符合逻辑的,不是任何我下楼或者回家的时候它们都要向我乞食。

不久前,我意识到这些猫也是事件驱动的。它们并不一定饿,只是在特定事件发生时,比如我离开很久再回到家后,它们便认为该吃东西了。在它们的意识中,这些事情的发生是有意义的,是与食物相关联的。

对UI来说,事件捕获的是用户与UI组件的交互方式。一个事件可能只是简单地点击一个组件,或者更复杂一点,是执行一系列特定的命令。JSF 利用JavaBean来通过事件对象和事件监听器处理事件,这与Swing类似。组件可以触发0个或者多个事件,开发人员(或者组件本身)也可以注册0个或多个监听器来处理这些事件。我喜欢将我们的猫想像成食物监听器——它们监听可以产生食物的事件。

事件是Web开发的基本手段。通常,开发Web应用需要开发人员考虑请求和响应——即下层无状态的HTTP协议的通信机制。考虑这些问题对很多应用来说是有益的,但是对业务应用来说,就有些不必要,并使应用过分地接近协议。

在编写JSF应用时,集成应用逻辑其实是对那些产生监听器能懂得的事件的组件分配适当的监听器。而不必考虑请求和响应。JSF支持用类似于面向接口的方式开发事件监听器,以及将任意方法注册为事件监听器的能力(只要它具有正确的方法签名)。

共有4种标准事件:值改变事件、动作事件、数据模型事件以及阶段事件。值改变事件是在用户修改组件的值后,由输入控件触发。动作事件在用户激活命令组件(比如按钮)时产生。数据模型事件在数据感知组件选择了一行要处理的数据时产生。阶段事件则在JSF处理HTTP请求时产生。

这些仅仅是框架所定义的标准事件,这并没有限制使用其他类型的事件。第三方或者你自己,都可以容易地支持不同类型的事件。

1.值改变事件

值改变事件是当用户在输入组件中输入新值时产生的。可以使用值改变监听器来处理值改变事件。

例如,假定在页面中有HtmlInputText和HtmlPanelGrid组件:

注意,对HtmlInputText组件来说,我们通过一个JSF EL表达式来指定valueChangedListener属性。该表达式指向后台bean中的值改变监听器方法myForm.processValueChanged。另外,HtmlPanelGrid被绑定到后台bean的changePanel属性以便在Java代码中操作它。网格的rendered属性设置为false,这样开始时它不会向用户显示。

当用户修改文本框中的值并提交表单时,JSF将产生值改变事件。然后由事件监听器来处理这个事件:

这个例子中,事件监听器方法将事件发送者(这里是HtmlInputText组件)的readOnly属性改为true,以便用户不能再修改该内容。然后将HtmlPanelGrid组件(绑定至changePanel属性)的 rendered属性改为true,以便页面重显时用户可以看见它。事件监听器也可以添加消息和执行其他JSF操作,还可执行应用逻辑。

除了使用事件监听器方法处理值改变事件外,也可以注册实现该接口的事件监听器类。不过,大多数情况下,将组件与特定方法相关联就足够了。

2.动作事件

动作事件在用户与表示命令或者用户动作的控件发生交互时产生。产生动作事件的组件称为动作源(action source),包括按钮和超链接之类的控件。动作事件由动作监听器处理。

有两类动作事件:一类要影响导航,而另一种则不影响。影响导航的动作监听器通常执行一些处理,然后向JSF的导航系统返回逻辑结果,以使后者能够选择下一个页面(也可以是当前正在显示的页面)。不影响导航的监听器,通常操作当前视图中的组件,或者执行一些修改模型对象或后台bean属性之类的操作,但并不改变当前正在访问的页面。结果是,页面通常在监听器执行后重新显示。

从技术上说,所有的导航都是仅由一个动作监听器处理的。这个监听器自动处理任何由组件产生的动作事件,所以不需手动注册。默认情况下,该监听器将其所有工作委托给后台bean中的动作方法进行处理,所以可以用不同的动作方法(action method)处理应用中的不同部分。通常,应用的大部分逻辑将位于这些方法中(动作监听器是可插入的,所以可以用根本不使用动作方法的监听器替换它)。

当组件触发动作事件时,这个默认的动作监听器将决定其结果字符串——比如"mainmenu"、"success"或"failure"等。这里有两种基本类型的结果:静态的和动态的。静态结果是在组件内声明的或者在代码中硬编码的字符串:

这个例子中,当用户点击HtmlCommandButton 时将产生静态结果"success",并产生动作事件,但没有调用任何动作方法。

动态结果是由动作方法自身返回的字符串,动作方法会依赖于它所执行的应用逻辑返回不同的结果。当在组件的action属性中使用JSF EL表达式时,动作监听器将查找该动作方法。下面是执行动作方法的HtmlCommandButton组件:

用户点击这个按钮,则产生一个动作事件,并且执行下列方法,以响应此事件:

基于voodoo应用逻辑,此动作方法将返回结果"success"或者"failure"。LoginForm是一个后台bean,其属性关联到页面中的输入控件值,并且通过受管bean创建工具来配置。

此例中没有处理登录的实际逻辑,但是动作方法可以操作JSF 组件、模型对象或者添加消息。它们也能完成其他一些有趣的任务,比如执行重定向、呈现响应(图像或者一些二进制数据)、添加事件,以及同数据库、EJB服务器或者Web服务进行通信。动作监听器使用动作方法的返回结果与导航系统挂钩,然后确定下一个页面。

当需要执行和导航无关的应用逻辑时,可以将动作监听器方法与组件相关联。与动作方法不同,动作监听器方法可以访问产生该事件的组件。来看这个例子:

如前一个例子,用户点击HtmlCommandButton将触发一个动作事件。但这次,执行的是动作监听器事件而不是动作事件:

该方法修改了产生该事件的按钮的标签值——虽然这并不十分有用。然而,重要的是其方法签名。它并不是不接受任何参数而返回结果字符串,动作监听器方法接受一个ActionEvent作为其参数,而不返回结果。这个方法执行后,页面将重新显示以应用该方法所做过的所有修改。

通常,使用事件监听器方法来进行会影响当前视图的改变。正如值改变监听器,也可以使用Java接口来实现动作监听器,虽然大多数情况下使用现有的后台bean中的方法已经足够。

3.数据模型事件

数据模型事件在数据感知组件要处理一个数据行时产生。最常见的产生这种事件的方式是,通过显示可选项目列表的组件来产生,比如HtmlDataTable,它是十分基本的“数据网格”组件。与值改变监听器和动作事件监听器不同,数据模型事件监听器必须实现Java接口。

数据模型事件和其他事件有些不同,它不是由某个实际的UI组件产生的。相反,它是由DataModel实例产生的,而该实例是数据感知组件内部使用的模型对象。数据模型对象是列表、数组、结果集以及其他数据源的包装类。因为技术上事件是由模型对象产生而不是UI组件产生,所以不能在JSP中为组件自身注册监听器,而必须在Java代码中进行注册:

这个例子中,首先创建一个新的HtmlDataTable组件,然后使用预先存在的JDBC ResultSet创建新的ResultSetDataModel。接下来添加新的DataModelListener(实现为一个inner类)到ResultSetDataModel中,并且设置数据模型为HtmlDataTable组件的值。其结果是,每次HtmlDataTable都通过数据模型中的新行迭代一次,监听器也要执行一次。这通常发生在组件被显示时。因为数据模型事件要这样的多次产生,所以它们通常用于开发数据驱动的组件,而不是用在普通的应用中。

4.阶段事件

JSF应用接收到请求时,它要对请求进行共有6个步骤的称为请求处理生命周期(request processing lifecycle)的处理。在此过程中,JSF要恢复请求视图,转换请求参数到组件值,验证输入,更新后台bean 或模型对象,调用动作监听器,然后返回响应给用户。阶段事件在此生命周期中的每一个步骤或阶段之前和之后产生(我们将在2.2节详细讨论请求处理生命周期)。

阶段事件是由JSF自己产生的,而不是由UI组件产生的,并且需要实现Java接口来注册事件监听器。它们通常被JSF实现内部使用,但是有时在应用开发中也很有用。例如,它可以用来初始化显示之前的后台bean(Sun的Java Studio Creator自动允许后台bean直接处理阶段事件)。

下面是注册在显示之前执行的阶段监听器的例子:

在此例中,添加了新的PhaseListener实例到现有的Lifecycle实例中(Lifecycle表示一个请求处理生命周期)。监听器的phaseId 属性告诉生命周期应该何时处理此事件。在这里,监听器将在视图被呈现时执行(呈现器响应阶段)。beforePhase方法将在视图被显示前执行,而afterPhase方法则在视图被显示后执行。在beforePhase中,基于来自于QuoteService类的最新值更新priceQuote属性。priceQuote属性的这种修改以便它在相应视图中被组件正确显示。

如你所见,在JSF应用中,无需担心任何有关协议的细节——只需考虑事件和监听器。当然这并不意味着你不必理解HTTP是如何工作的,但是这已经使得日常工作简化了很多(如果你喜欢全盘控制,也别担心——如你需要,仍然可以访问Servlet API)。事件和监听器是开发JSF应用的基础,你在本书的第三部分和第四部分都能找到一些例子,但是针对应用开发人员的特殊讨论是在第11章。

2.1.7  消息

上述有关JSF的概念的讨论是很棒的,但是如果发生了错误又该如何呢?开发UI的一个大问题是,如何正确地显示错误信息。它们可被分为两类:应用错误(业务逻辑、数据库或连接错误等)以及用户输入错误(比如无效的文本或者空字段)。

通常,应用错误将产生新的页面来显示错误信息;而用户输入错误则重新显示调用页面,并在其中显示指示错误的信息。通常需要在不同的页面显示相同的错误信息,这样就必须保证用户所见到的信息是一致的。你并不希望在一个页面中显示“请输入你的电话号码”,而在另一个页面中显示“需要输入电话号码”。

JSF提供了消息机制来帮助解决这个问题。消息由概要文本、详细文本和严重级别组成。消息也可以根据用户的当前语言自动裁减(假定你的应用支持该语言)。应用中的各部分——包括UI组件、验证器、转换器或者事件监听器,在处理请求时都可以添加消息;JSF则维护当前所有消息的列表。你总是可以通过组件或者应用代码获得当前栈中的消息来进行处理。

消息并不是必需指示错误;它们也可以是指示信息。例如,动作监听器方法就可以添加消息以指示成功添加了新记录。消息自身也可以与特定的组件相关联(针对输入错误)或者根本不与任何组件相联(针对应用消息)。

你可以使用HtmlMessage组件来显示与特定组件相关联的错误信息。你也许还记得Hello, world! 应用中HtmlMessage是这样使用的:

这个标签显示由helloInput 输入组件产生的所有错误消息(该组件必须在同一页面中声明)。也可以使用HtmlMessages组件显示所有消息,或者那些没有关联到特定组件的消息。

消息提供各种方便的方式来向用户交流错误信息和其他信息。它们是JSF的验证和类型转换特征的内部组成——无论何时验证器遇到不正确的值或者转换器处理了不正确的类型,都要产生错误消息。这是一个很好的与用户交流信息的方式,而你不用担心如何显示它们;只需在动作监听器方法中简单的创建一个消息,视图就会显示它。你也可以在第6章找到如何定制应用消息,以及在第11章找到如何在Java代码中创建它们。

2.1.8  导航

到目前为止,我们所讨论的所有概念都是关于与单个页面交互的。如果仅是一个单一页面,编写Web应用就好似蛋糕一块,但实际情况关非如此。Web应用有多个页面,并且必须以某种方式来在它们之间切换。从一个页面切换到另一个页面就称为是导航。

JSF有个非常优雅的导航系统。基于动作方法的逻辑结果,导航处理器负责决定装入哪一个页面。对给定的页面,导航规则定义如何理解逻辑结果,以及基于这些逻辑结果装入哪个页面。结果和页面之间的特定映射称为导航案例。导航规则在JSF配置文件中定义。下面是login.jsp页面的导航规则,具有两个案例—"success"和"failure"结果:

    你可以看到,每个案例将结果对应到特定的页面——不需要编写代码。如果使用Struts之类的框架,这也是JSF与之类似的一个便捷特征。所有导航案例通常都保存在一处,这意味着任何修改都可在一个中心位置进行,而不是在多个页面中进行。关于导航将在第 3章详细讨论,你也可以在第二部分和第三部分找到很多例子。

 
原创粉丝点击