本文是基于JSF规范的翻译而来,并省掉一些无关紧要的章节。如有不当之处请大家指正。
作者:youfly email:seedcloned-pub@yahoo.com.cn
转载请注明出处:www.jfuns.com www.jfuns.cn http://blog.csdn.net/youfly
2.5 Concepts that impact several lifecycle phases
本章试图通过给读者提供一个影响“请求处理生命周期”各个阶段的一些复杂概念的 “big picture”视图。
2.5.1 Value Handling
在基本原理层面,JavaServer Faces提供了一个从用户那里获取数据并提供给模型层处理的一个途径。关于从用户到模型的值处理流在这份规范的其他地方描述,但是可以从这里得到一个整体的认识。下面的描述假定是JSP/HTTP的情形,并且所有的组件都拥有Renderers。
2.5.1.1 Apply Request Values Phase
当用户点击按钮并且引发提交,将发送name=value对形式的HTTP请求POST数据到服务器中。这就进入了JSF的“请求处理生命周期”,并且最终我们进入了Apply Request Values阶段。在这个阶段视图中的每个组件的每个Renderer上的decode()方法被调用。Renderer从请求中取出值,并将这些值传递给组件的setSubmittedValue()方法,当然,这些组件应该是EditableValueHolder的实例。如果组件的”immediate”属性被设置成true,我们在decoding后将立即执行验证。请阅读下面的内容以得到执行验证时所发生的事情。
2.5.1.2 Process Validators Phase
视图根的processValidators()方法被调用。对于视图中的每一个EditableValueHolder,要是”immediate”属性没有设置,我们将执行视图中的每一个UIInput的验证。否则,则表示验证已经被执行,这个阶段不需要作任何处理。
2.5.1.3 Executing Validation
请阅读javadoc文件当到UIInput.validate()方法更为详细的信息,但基本上,这个方法从组件中获取提交的值(在Apply Request Values期间被设置) ,取得组件的Renderer并调用它的getConvertedValue()方法,并传递到取的值给这个方法。如果发生数据转换错误,则按照这个getConvertedValue方法的javadoc文档描述进行处理。否则,所有和这个组件绑定的validators被请求来验证转换的值是否合法。如果发生验证错误,则按照Validator.validate()方法的javadoc描述进行处理。转换值通过组件的setValue()设置到组件中,如果值被改变,则触发ValueChangeEvent事件。
2.5.1.4 Update Model Values Phase
视图中的每一个UIInput组件,它们的updateModel方法被调用。这个方法只是在验证期间组件的本地值(Local value)被设置并且页面的作者配置了设置这个组件值到模型(Model)层的时候才有效。这个阶段只是简单的根据页面作者的配置将UIInput组件转换成功的本地值设置到模型(Model)中。在试图将值设置到模型(Model)层时所发生的任何错误都按照UIInput.updateModel()方法的javadoc文档描述来处理。
2.5.2 Localization and Internationalization (L10N/I18N)
JavaServer Faces被完全的国际化,JavaServer Faces提供的I18N能力是基于Servlet,JSP及JSTL规范提供的I18N概念。
I18N happens at several points in the request processing lifecycle, but it is easiest to explain what goes on by
breaking the task down by function.
2.5.2.1 Determining the active Locale
JSF用活动Locale的概念来查找所有的本地化资源。转换器必须使用这个Locale来执行他们的转换。Locale保存在当FacesContext的UIViewRoot的locale JavaBeans属性中。应用开发者可以通过配置文件WEB-INF/faces-config.xml 来告诉JSF当前应用所支持的locales。例如:
<faces-config>
<application>
<locale-config>
<default-locale>en</default-locale>
<supported-locale>de</supported-locale>
<supported-locale>fr</supported-locale>
<supported-locale>es</supported-locale>
</locale-config>
</application>
这个应用的默认locale是en,但是它支持de,fr及es等locales。这些元素将引起Application实例封装Locacle数据。请阅读javadocs文档以得到更为详细的信息。
在ViewHandler执行createView()方法期间,决定并设置UIViewRoot对象的locale。这个方法必须根据应用所支持的Locale及用户参数来决定当前的活动Locale。请阅读javadocs文档以得到更为详细的信息。应用能够直接的调用UIViewRoot.SetLocale()方法,但页面的作者也可以通过<f:view>标记来覆盖UIViewRoot的locale属性。这个属性值必须以language[{-
|_}country[{-|_}variant]]的形式指定,且不能包含冒号,例如”ja_JP_SJIS”。片段之间的分隔符可以是'-' 或者 '_'。
在所有使用JSP的情况下,活动locale通过Config.FMT_LOCALE这个key被设置进“request scope”的JSTL类javax.servlet.jsp.jstl.core.Config类中。
2.5.2.2 Determining the Character Encoding
请求和响应的字符集编码通下面列的方法被设置和说明。
一个初始的JSF web应用请求,它的字符集编码是否保持未更改状态,依赖于request对象正确的解析request参数。
在render-response阶段的开始,ViewHandler必须确保response Locale设置到UIViewRoot中,例如在servlet环境中调用ServletResponse.setLocale()方法。设置response的Locale将影响response的字符集编码,请阅读Servlet和Porlet规范得到更为详细的信息。
在render-response阶段的结束,ViewHandler必须使用众所周知的实现依赖的key将response(例如servlet或者portlet Response)的字符集编码保存在session(仅仅在session存在时)中。
在后续的postback处理中,任何调用访问request parameters的ExternalContext方法被调用前,ViewHandler必须检查Content-Type头,取得charset属性,并用取到的值设置request对象的字符集编码。如果Content-Type头没有包含charset属性,则用先前保存在session(仅仅在session存在时)中的字符集编码设置request对象的编码。如果没有任何字符集编码找到,则应该保持请求的字符集编码不变。
上面的算法提供了一个让有特殊需求应用可以调整request和response字符的机制。如例JSP页面可通过page指示符设置页面的contentType属性使用固定的字符集编码,请阅读servlet,portlet及jsp规范得到更为详细的信息。
注意,Servlet 2.4及JSP 2.0之前的字符集编码规则并不精确,因此我们要特别注意不同容器之间的移植。
2.5.2.3 Localized Text
没有直接支持本地化文本的API,但JSP层提供了一个便利的标记来将Resource Bundle转换成java.util.Map,并保存它到命名空间中,因此我们可以得到它。这一节描述如果将本地化的文本显示给最终用户。这些包括图片,标签,按钮文本,提示等。自从所有的JSF组件允许从模型层得到它们的显示信息,因此就容易的通过模块层进行本地化。为了方便,JSF提供了一个<f:loadBundle>标记,这个标记能够将ResourceBundle载入到Map中,并将它request scope的命名空间中,因此在模型层就可以用相同的机制来访问这些数据。例如:
<f:loadBundle basename=”com.foo.industryMessages.chemical” var=”messages” />
<h:outputText value=”#{messages.benzene}” />
这将引起名称为com.foo.industryMessages.chemical 的ResourceBundle载入到Map中,并保存到key为messages request属性中。本地化内容能够通过一般的值表达式语法取出。
2.5.2.4 Localized Application Messages
这节描述了JSF如如何处理在转换,验证,或其他application action时发送的错误及提示信息。JSF类javax.faces.application.FacesMessage提供了封装的消息摘要,详细及级别信息。JSF实现必须提供包含所有标准消息的javax.faces.Messages ResourceBundle。下面列出所有的标准消息(和非标准的预留消息文本)key:
■ javax.faces.component.UIInput.CONVERSION -- {0}: Conversion error occurred
■ javax.faces.component.UIInput.REQUIRED -- {0}: Validation Error: Value is required
■ javax.faces.component.UIInput.UPDATE -- {0}: An error occurred when processing your
submitted information
■ javax.faces.component.UISelectOne.INVALID -- {0}: Validation Error: Value is not valid
■ javax.faces.component.UISelectMany.INVALID -- {0}: Validation Error: Value is not
valid
■ javax.faces.converter.BigDecimalConverter.DECIMAL={2}: ''{0}'' must be a signed
decimal number.
■ javax.faces.converter.BigDecimalConverter.DECIMAL_detail={2}: ''{0}'' must be a
signed decimal number consisting of zero or more digits, that may be followed by a
decimal point and fraction. Example: {1}
■ javax.faces.converter.BigIntegerConverter.BIGINTEGER={2}: ''{0}'' must be a number
consisting of one or more digits.
■ javax.faces.converter.BigIntegerConverter.BIGINTEGER_detail={2}: ''{0}'' must be a
number consisting of one or more digits. Example: {1}
■ javax.faces.converter.BooleanConverter.BOOLEAN={1}: ''{0}'' must be 'true' or 'false'.
■ javax.faces.converter.BooleanConverter.BOOLEAN_detail={1}: ''{0}'' must be 'true' or
'false'. Any value other than 'true' will evaluate to 'false'.
■ javax.faces.converter.ByteConverter.BYTE={2}: ''{0}'' must be a number between 0 and
255.
■ javax.faces.converter.ByteConverter.BYTE_detail={2}: ''{0}'' must be a number between
0 and 255. Example: {1}
■ javax.faces.converter.CharacterConverter.CHARACTER={1}: ''{0}'' must be a valid
character.
■ javax.faces.converter.CharacterConverter.CHARACTER_detail={1}: ''{0}'' must be a
valid ASCII character.
■ javax.faces.converter.DateTimeConverter.DATE={2}: ''{0}'' could not be understood as a
date.
■ javax.faces.converter.DateTimeConverter.DATE_detail={2}: ''{0}'' could not be
understood as a date. Example: {1}
Chapter 2 Request Processing Lifecycle 2-19
■ javax.faces.converter.DateTimeConverter.TIME={2}: ''{0}'' could not be understood as a
time.
■ javax.faces.converter.DateTimeConverter.TIME_detail={2}: ''{0}'' could not be
understood as a time. Example: {1}
■ javax.faces.converter.DateTimeConverter.DATETIME={2}: ''{0}'' could not be
understood as a date and time.
■ javax.faces.converter.DateTimeConverter.DATETIME_detail={2}: ''{0}'' could not be
understood as a date and time. Example: {1}
■ javax.faces.converter.DateTimeConverter.PATTERN_TYPE={1}: A 'pattern' or 'type'
attribute must be specified to convert the value ''{0}''.
■ javax.faces.converter.DoubleConverter.DOUBLE={2}: ''{0}'' must be a number consisting
of one or more digits.
■ javax.faces.converter.DoubleConverter.DOUBLE_detail={2}: ''{0}'' must be a number
between 4.9E-324 and 1.7976931348623157E308 Example: {1}
■ javax.faces.converter.EnumConverter.ENUM={2}: ''{0}'' must be convertible to an enum.
■ javax.faces.converter.EnumConverter.ENUM_detail={2}: ''{0}'' must be convertible to an
enum from the enum that contains the constant ''{1}''.
■ javax.faces.converter.EnumConverter.ENUM_NO_CLASS={1}: ''{0}'' must be
convertible to an enum from the enum, but no enum class provided.
■ javax.faces.converter.EnumConverter.ENUM_NO_CLASS_detail={1}: ''{0}'' must be
convertible to an enum from the enum, but no enum class provided.
■ javax.faces.converter.FloatConverter.FLOAT={2}: ''{0}'' must be a number consisting of
one or more digits.
■ javax.faces.converter.FloatConverter.FLOAT_detail={2}: ''{0}'' must be a number
between 1.4E-45 and 3.4028235E38 Example: {1}
■ javax.faces.converter.IntegerConverter.INTEGER={2}: ''{0}'' must be a number
consisting of one or more digits.
■ javax.faces.converter.IntegerConverter.INTEGER_detail={2}: ''{0}'' must be a number
between -2147483648 and 2147483647 Example: {1}
■ javax.faces.converter.LongConverter.LONG={2}: ''{0}'' must be a number consisting of
one or more digits.
■ javax.faces.converter.LongConverter.LONG_detail={2}: ''{0}'' must be a number between
-9223372036854775808 to 9223372036854775807 Example: {1}
■ javax.faces.converter.NumberConverter.CURRENCY={2}: ''{0}'' could not be understood
as a currency value.
■ javax.faces.converter.NumberConverter.CURRENCY_detail={2}: ''{0}'' could not be
understood as a currency value. Example: {1}
■ javax.faces.converter.NumberConverter.PERCENT={2}: ''{0}'' could not be understood as
a percentage.
2-20 JavaServer Faces Specification • May 2006
■ javax.faces.converter.NumberConverter.PERCENT_detail={2}: ''{0}'' could not be
understood as a percentage. Example: {1}
■ javax.faces.converter.NumberConverter.NUMBER={2}: ''{0}'' is not a number.
■ javax.faces.converter.NumberConverter.NUMBER_detail={2}: ''{0}'' is not a number.
Example: {1}
■ javax.faces.converter.NumberConverter.PATTERN={2}: ''{0}'' is not a number pattern.
■ javax.faces.converter.NumberConverter.PATTERN_detail={2}: ''{0}'' is not a number
pattern. Example: {1}
■ javax.faces.converter.ShortConverter.SHORT={2}: ''{0}'' must be a number consisting of
one or more digits.
■ javax.faces.converter.ShortConverter.SHORT_detail={2}: ''{0}'' must be a number
between -32768 and 32767 Example: {1}
■ javax.faces.converter.STRING={1}: Could not convert ''{0}'' to a string.
■ javax.faces.validator.DoubleRangeValidator.MAXIMUM -- {1}: Validation Error: Value is
greater than allowable maximum of ‘’{0}’’
■ javax.faces.validator.DoubleRangeValidator.MINIMUM -- {1}: Validation Error: Value is
less than allowable minimum of ‘’{0}’’
■ javax.faces.validator.DoubleRangeValidator.NOT_IN_RANGE -- {2}: Validation Error:
Specified attribute is not between the expected values of {0} and {1}.
■ javax.faces.validator.DoubleRangeValidator.TYPE -- {0}: Validation Error: Value is not of
the correct type
■ javax.faces.validator.LengthValidator.MAXIMUM -- {1}: Validation Error: Value is
greater than allowable maximum of ‘’{0}’’
■ javax.faces.validator.LengthValidator.MINIMUM -- {1}: Validation Error: Value is less
than allowable minimum of ‘’{0}’’
■ javax.faces.validator.LongRangeValidator.MAXIMUM -- {1}: Validation Error: Value is
greater than allowable maximum of ‘’{0}’’
■ javax.faces.validator.LongRangeValidator.MINIMUM -- {1}: Validation Error Value is
less than allowable minimum of ‘’{0}’’
■ javax.faces.validator.LongRangeValidator.NOT_IN_RANGE={2}: Validation Error:
Specified attribute is not between the expected values of {0} and {1}.
■ javax.faces.validator.LongRangeValidator.TYPE -- {0}: Validation Error: Value is not of
the correct type
The following message keys are deprecated:
■ javax.faces.validator.NOT_IN_RANGE -- Specified attribute is not between the expected
values of {0} and {1}
■ 调用当前应用Application实例的getMessageBundle()方法来判断应用是否定义了指定名字的Resource bundle。如果已定义,则载入这个ResourceBundle,并通过它来查找消息。
■ 如果没有定义,则查找javax.faces.Messages这个resource bundle。
■ 不管哪种情况,如果消息找到,则根据上面的约定创建一个FacesMessage实例。
2.5.3 State Management
JavaServer Faces引入了一个强大的灵活的系统来保存及恢复请求和服务器之间视图状态。有必要从不同的观点来介绍状态管理。对于页面的作者,状态管理是透明的发生的。对于应用组装者,可以通过ServletContext InitParameter的javax.faces.STATE_SAVING_METHOD参数设置状态管理信息是保存在客户端还是服务端。这个参数指定了JSF实现的状态管理结果。
2.5.3.1 State Management Considerations for the Custom Component Author
组件的开发者不能在运行期得到组件状态的保存方法,但他们必须理解组件的状态管理。如 “The javax.faces.component package”节的FIGURE 4-1所示, 所有的JSF组件都实现StateHolder接口。作为一个重要的实现StateHolder的标准组件。一个自定义的直接扩展UICmponent的组件,并且没有扩展其他标准组件则需要手工实现StateHolder接口。请查看3.2.4 “StateHolder”节得到更多的信息。一个没有从标准组件扩展并且维护了自身状态的自定义组件,需要特别注意对父类的状态管理,以正确的实现StateHolder。特别的,调用saveState()必须不以任何方式改变状态。子类负责父类的状态保存和恢复。考虑这个例子,我的自定义组件表现为“slider” ui部件,同样的,它需要跟踪最大值 ,最小值,以及当前值等状态。
public class Slider extends UISelectOne {
protected Integer min = null;
protected Integer max = null;
protected Integer cur = null;
// ... details omitted
public Object saveState(FacesContext context) {
Object values[] = new Object[4];
values[0] = super.saveState(context);
values[1] = min;
values[2] = max;
values[3] = cur;
}
public void restoreState(FacesContext context, Object state) {
Object values[] = (Object {}) state; // guaranteed to succeed
super.restoreState(context, values[0]);
min = (Integer) values[1];
max = (Integer) values[2];
cur = (Integer) values[3];
}
注意我们在相应的地方调用了super.saveState()和 super.restoreState()方法。这绝对生死攸关的!如果没有成功的完成这个调用将影响组件的正常工作。
2.5.3.2 State Management Considerations for the JSF Implementor
状态管理功能的目的是让页面的作者,应用组装及组件作者的工作更为容易。然而,有些地方也存在着复杂性,JSF的实现者是一个幸运的角色。这里是一些关键参与者总的概述。请阅读javadocs文档得到每个类的更多信息。
Key Players in State Management
■ ViewHandler 状态管理系统的入口点。使用帮助类StateManager来完成它的实际工作。在JSP的情景下,委派标记处理器<f:view>来完成这些功能。
■ StateManager 提供了状态保存的实际工作。使用帮助类ResponseStateManager来呈现特定技术的结果。
■ ResponseStateManager 提供呈现特定技术的状态管理结果。
■ UIComponent 指示处理保存和恢复每个组件的状态。