本文是基于JSF规范的翻译而来,并省掉一些无关紧要的章节。如有不当之处请大家指正。
作者:youfly email:seedcloned-pub@yahoo.com.cn
转载请注明出处:www.jfuns.com www.jfuns.cn http://blog.csdn.net/youfly
8Rendering Model
JavaServer Faces支持两种编程模型来完成从请求中解码出组件值及将编码组件值编码成输出响应,这两种编程模型分别是直接实现和委派实现模型。当使用直接实现模型时,组件自己来完成解码和编码工作。当使用委派实现模型时,这些操作将委派给组件对应的Renderer(根据组件的rendererType属性)实例来完成。这就允许应用程序在处理组件的时候可以不依赖于组件呈现给用户的形式,通过简单的操作(选择具体的RenderKit)来根据具体的客户端设置及本地用户的特点来客户化应用。
组件编写者,应用开发者,工具提供者及JSF实现一般都会提供一个或者多个RenderKit实现(根据对应的Renderer实例库)。在很多情况下,这些类都是随着RenderKit所支持的UIComponent类一起提供。页面的作者不需要直接处理RenderKits,因为他们仅仅需要选择具体页面对应的RenderKit ID及每个UIComponent rendererType属性,这个属性用来确定对应的Renderer。
8.1 RenderKit
一个RenderKit实例可以随意的关联到一个视图,并支持组件使用委派实现模型来完成组件值的编码解码工作。JSF组件必须提供默认的RenderKit实例(由下面描述的字符串常量RenderKitFactory.HTML_BASIC_RENDER_KIT对应的Render kit ID所命名),用来支持没有任何RenderKit被指定的默认实现。
public Renderer getRenderer(String family, String rendererType);
返回一个和组件family及RendererType(如果存在)关联的Renderer实例,Return the Renderer instance corresponding to the specified component family and
rendererType (if any), which will typically be the value of the rendererType
property of a UIComponent about to be decoded or encoded.
public void addRenderer(String family, String rendererType,Renderer renderer);
如果一个应用希望超越JSF实现默认实现提供的标准RenderKit的能力,则它可能通过创建自己的RenderKit实例并通过RenderKitFactory实例(参考8.4“RenderKitFactory”)注册这些实例,或者通过集成附加的(或者替换)支持Renderer实例到已存在的RenderKit实例来实现。例如,应用程序可以在程序启动的时候通过JSF实现提供的标准RenderKit注册它们需要的自定义组件及Renderers。参考10.4.6 “Example Application Configuration Resource”提供的关于faces-config.xml配置文件的例子,它定义了两个附加的Renderer实例,并注册到默认的RenderKit中。
public ResponseWriter createResponseWriter(Writer writer, String contentTypeList, String characterEncoding);
使用提供的Writer来创建一个指定字符集编码的ResponsWriter实例。
contentTypeList参数是当前响应的Content type的"Accept header style"列表,或者null(如果RenderKit需要自己选择最佳的)。在当前版本中,标准的Render-kit接受的参数包括所有有效的"Accept header style"字符串,这些字符串包括text/html, application/xhtml+xml, application/xml或者text/xml,将来可能会改变。RenderKit必须支持从Accept HTTP header过来的值以处理这个参数,因此也要求根据Accept header规范来解析。请参见14.1的RFC2616(HTTP 1.1 RFC)中关于Accept header的规范。
实现者通过javax.faces.servlet.ServletResponse类的getCharacterEncoding()方法取到这个方法所要求characterEncoding参数值。一旦我们获取了Writer(由于它最终被传递给了这个方法),正如我们所知的,就不能在输出响应期间再改变它的字符集编码。 请参考6.4 “ResponseWriter”节。
public ResponseStream createResponseStream(OuputStream out);
使用提供的OutputStream来创建一个新的ResponseStream实例。
public ResponseStateManager getResponseStateManager();
返回一个ResponseStateManager来处理呈现技术具体的状态管理结果。
8.2 Renderer
在组件的rendererType属性值为null的时候,则由Renderer实现提供“请求处理生命周期”Apply Request Values及Render Response阶段的组件解码编码功能。
public void decode(FacesContext context, UIComponent component);
组件采用委派实现模型时,为了将进入的请求信息转换成为组件的本地值(local value),这个方法将在“请求处理生命周期”的Apply Request Values阶段被调用。请阅读Renderer.decode()方法的javadocs来得到更详细的信息。
public void encodeBegin(FacesContext context, UIComponent component) throws IOException;
public void encodeChildren(FacesContext context, UIComponent component) throws IOException;
public void encodeEnd(FacesContext context, UIComponent component) throws IOException;
组件采用委派实现模型时,这些方法将在“请求处理生命周期”的Render Response阶段被调用。这些方法职责和在采用直接实现模型时UIComponent组件对应的encodeBegin(),encodeChildren()及encodeEnd()方法类似(在3.1.12 “Component Specialization Methods”节及对应的javadocs有详细的说明)。
public String convertClientId(FacesContext context, String clientId);
将组件生成的clientId转换成适合传输到客户端的clientId。
public boolean getRendersChildren();
返回当前Renderer是否负责呈现对应组件的子组件。
public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException;
试图将先前保存的状态信息转换成这个组件所要求类型的对象(使用这个组件注册的Converter)。如果转换成功,这个方法应该返回一个新值,如果不成功则抛出ConverterException异常。
8.3 ResponseStateManager
ResponseStateManager是一个javax.faces.application.StateManager帮助类,它知道如何用特定的表现技术来生成响应。它是一个singleton抽象类。这个类知道何在状态的结构,是否使用隐藏域,session,或都将两者结合使用。
public Object getState(FacesContext context);
实现必须检查当前请求并返回一个传递给writeState()方法的组件树状态对象。
public void writeState(FacesContext context, Object state) throws
IOException;
使用当前ResponseWriter对象将参数state输出到输出流中。
如果状态被输出到隐藏域,实现者必须进行必要的字符串替换,使值可以安全的包含在HTTP请求参数中。
如果状态保存方式是client,实现可以将状态加密后保存到客户端中。我们建议状态对客户端是不可阅读和不可篡改的。
用字符串常量ResponseStateManager.RENDER_KIT_ID_PARAM指定的名称输出当前和ResponseStateManager相关联的render kit ID。render kit ID不能输出,如果:
■ 如果这是一个通过Application.getDefaultRenderKitId()方法得到的默认的render kit ID,或者
■ render kit ID的值是
RenderKitFactory.HTML_BASIC_RENDER_KIT并且
Application.getDefaultRenderKitId()返回null。
ResponseStateManager.RENDER_KIT_ID_PARAM是被ViewHandler.calculateRenderKitId()默认实现用来得到render kit ID请求参数名。
public boolean isPostback(FacesContext context);
如果当前请求是postback则返回true。默认实现返回true,如果当前ResponseStateManager实例在先前请求的状态输入中指定了当前请求是postback。否则返回false。
请参阅 7.7.7 “ResponseStateManager”
8.4 RenderKitFactory
对于每一个运行在servle或portlett容器里的基于JSF web应用,都必须要有一个单实例的javax.faces.render.RenderKitFactory。工厂实例能够被JSF或者程序代码获取到,通过执行如下的代码:
RenderKitFactory factory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY)
RenderKitFactory实现类支持下列的一些方法:
public RenderKit getRenderKit(FacesContext context, String renderKitId);
返回指定render kit ID的RenderKit实例,该实例可能根据FacesContext(也可能为null)指定的特征被客户化。例如,一个实现可能需要根据请求的“User-Agent”头来选择不同的RenderKit,或者根据建立的响应视图的Local来选择不同的RenderKit。注意基于这种特性的应用不能保证在不同的JSF实现间移植。
JSF实现必须为字符串常量RenderKitFactory.HTML_BASIC_RENDER_KIT指定的默认Render kit ID提供RenderKit实例。附加的Render kit ID和对应实例也有效。
public Iterator<String> getRenderKitIds();
方法返回这个工厂所支持的render kit id的Iterator。这个Iterator里必须包含RenderKitFactory.HTML_BASIC_RENDER_KIT指定的值。
public void addRenderKit(String renderKitId, RenderKit renderKit);
根据指定的render kit id注册一个RenderKit实例,替换先前已存在的render kit id对应的实例。
8.5 Standard HTML RenderKit Implementation
为了确保应用程序的可移植性,所有的JSF实现都要求支持一个RenderKit及相应的Renderers,在本章将会见到一些需求定义,来生成HTML4.01兼容的文本标记JSF实现和其他部分,可能也提供附加的RenderKit库,或者那些在应用启动的时候添加到标准RenderKit里的Renderers,但是应用必须确保使用它的Web应用可以使用标准Renderers。
在本规范题名为“The Standard HTML RenderKit”节的一集外部HTML页面里指定了标准RenderKit要求的行为。这些页面里描述的行为是标准化的,所有的JSF实现都要求实现这些行为。
8.6 The Concrete HTML Component Classes
在先前章节提到的每一个有效的UIConopnent子类和标准Renderer组合,都有一个具体实现类定义在javax.faces.component.html包里。这个包里的每一个类都是对应javax.faces.component包里类的子类,并且添加了表现依赖的强类型JavaBeans属性。
表8-1 具体的HTML组件类
javax.faces.component类 | renderer-type | javax.faces.component.html类 |
UICommand | javax.faces.Button | HtmlCommandButton |
UICommand UIData | javax.faces.Link javax.faces.Table | HtmlCommandLink HtmlDataTable |
UIForm | javax.faces.Form | HtmlForm |
UIGraphic | javax.faces.Image | HtmlGraphicImage |
UIInput | javax.faces.Hidden | HtmlInputHidden |
UIInput | javax.faces.Secret | HtmlInputSecret |
UIInput | javax.faces.Text | HtmlInputText |
UIInput | javax.faces.Textarea | HtmlInputTextarea |
UIMessage | javax.faces.Message | HtmlMessage |
UIMessages | javax.faces.Messages | HtmlMessages |
UIOutput | javax.faces.Format | HtmlOutputFormat |
UIOutput | javax.faces.Label | HtmlOutputLabel |
UIOutput | javax.faces.Link | HtmlOutputLink |
UIOutput | javax.faces.Text | HtmlOutputText |
UIPanel | javax.faces.Grid | HtmlPanelGrid |
UIPanel | javax.faces.Group | HtmlPanelGroup |
UISelectBoolean | javax.faces.Checkbox | HtmlSelectBooleanCheckbox |
UISelectMany | javax.faces.Checkbox | HtmlSelectManyCheckbox |
UISelectMany | javax.faces.Listbox | HtmlSelectManyListbox |
UISelectMany | javax.faces.Menu | HtmlSelectManyMenu |
UISelectOne | javax.faces.Listbox | HtmlSelectOneListbox |
UISelectOne | javax.faces.Menu | HtmlSelectOneMenu |
UISelectOne | javax.faces.Radio | HtmlSelectOneRadio |
作为javax.faces.component包里的标准组件,每一个HTML组件实现类都必须定义一个static public final名COMPONENT_TYPE值为“javax.faces.”+类名的字符串常量。HTML组件不需要定义COMPONENT_FAMILY常,或者覆盖从它的父类继承下来的getFamily()方法。