23

When I am learning custom component development in JSF, I got confused with the relationship between component family, component type and renderer type. For example, I registered a renderer and a custom component as shown below.

faces-config.xml:

<component>
    <component-type>study.faces.Div</component-type>
    <component-class>javax.faces.component.UIPanel</component-class>
</component>

<render-kit>
    <render-kit-id>HTML_BASIC</render-kit-id>
    <renderer>
        <component-family>javax.faces.Panel</component-family>
        <renderer-type>study.faces.DivRenderer</renderer-type>
        <renderer-class>com.study.ui.DivRenderer</renderer-class>
    </renderer>
</render-kit>

I also registered a new tag in my.taglib.xml file as below:

<tag>
    <tag-name>div</tag-name>
    <component>
        <component-type>study.faces.Div</component-type>
        <renderer-type>study.faces.DivRenderer</renderer-type>
    </component>
</tag>

This configuration works very well. However, I didn't understand why the line <component-family>javax.faces.Panel</component-family> is required on renderer registration. In my.taglib.xml, the component and the renderer is connected and, IMHO, it should have been enough to select an appropriate renderer for the component. What is the use of the additional parameter <component-family>?

I did Google researches and all the answers I got say like "One renderer can be used to render multiple components. These components belong to one family". But these statements didn't clear my confusion up. Can someone explain the relationship between component type, component family and renderer selection strategy? (Hopefully with a good example.)

4

2 回答 2

39

渲染器由组件系列选择,而不是您似乎期望的组件类型。

让我们引用JSF 2.0 规范

3.1.2 组件类型

虽然不是 UIComponent 的属性,但组件类型是与每个子类相关的重要数据UIComponent,它允许Application实例创建UIComponent具有该类型的子类的新实例。有关组件类型的更多信息,请参见第 7.1.11 节“对象工厂”。

3.1.3 组件族

每个标准用户界面组件类都有一个组件族的标准值,用于查找与该组件关联的渲染器。泛型UIComponent类的子类通常会从其超类继承此属性,因此只期望超类的渲染器仍然能够处理专门的子类。

基本上,组件类型对于 JSF 是必需的,以便按Application#createComponent()方法创建组件。

UIComponent component = context.getApplication().createComponent("study.faces.Div");

这样做的好处是组件study.faces.Div不是编译时依赖,因此提供了运行时多态性和可插入性的可能性(如果您熟悉JDBC 的Class#forName()机制及其工厂,那么您会更好地理解该部分)。

每个组件类型都属于一个可以由一个或多个组件组成的族。渲染器是根据组件族和渲染器类型选择的RenderKit#getRenderer()

Renderer renderer = context.getRenderKit().getRenderer(component.getFamily(), component.getRendererType());

渲染器不是根据组件类型和渲染器类型来选择的。这允许对属于组件系列的多个组件类型重用渲染器。否则,您需要为每个组件注册一个渲染器,即使这些组件可以共享相同的渲染器。

以下faces-config.xml条目

<component>
    <component-type>study.faces.Div</component-type>
    <component-class>javax.faces.component.UIPanel</component-class>
</component>

告诉 JSFApplication无论何时要创建给定组件类型的组件,都应该创建给定组件类的实例。组件系列未在其中指定,因为component.getFamily().

以及以下faces-config.xml条目

<render-kit>
    <renderer>
        <component-family>javax.faces.Panel</component-family>
        <renderer-type>study.faces.DivRenderer</renderer-type>
        <renderer-class>com.study.ui.DivRenderer</renderer-class>
    </renderer>
</render-kit>

告诉 JSFRenderKit无论何时请求给定组件族和渲染器类型的渲染器,都应该返回给定渲染器类的实例。

以下.taglib.xml条目

<tag>
    <tag-name>div</tag-name>
    <component>
        <component-type>study.faces.Div</component-type>
        <renderer-type>study.faces.DivRenderer</renderer-type>
    </component>
</tag>

告诉 JSF(好吧,Facelets)给定的标签应该在视图根中创建一个给定组件类型的组件(其类在 中定义faces-config.xml),并且它的渲染器类型应该设置为给定的渲染器类型。请注意,组件类型用于选择渲染器,而是用于在视图根目录中创建组件。另请注意,渲染器类型条目是可选的。否则将使用组件自己的预定义渲染器类型。这允许使用不同的渲染器类型重用现有的组件类型。

于 2011-11-10T12:10:12.460 回答
3

javadocgetFamily():_

返回该组件所属的组件族的标识符。此标识符与 rendererType属性的值一起,可用于为此组件实例选择适当的 Renderer 。

JSF 规范中涵盖了维护者的基本原理:

组件系列

每个标准用户界面组件类都有一个组件族的标准值,用于查找与该组件关联的渲染器。通用UIComponent类的子类通常会从其超类继承此属性,因此只期望超类的渲染器仍然能够处理专门的子类。

我将此解释为提供了一些超出 Java 类类型的额外类型检查的能力(因为子类将始终是子类。)我不确定这到底有多大用处。

于 2011-11-10T11:57:23.520 回答