Facelets复合组件接口的多个实现



我正在开发一个JSF 2应用程序,该应用程序由一个核心组件组成,可以通过(通常是特定于客户端的)代码进行扩展。一般来说:应用程序的扩展部分先于核心部分。

对于 Java 代码,这是通过使用常规机制完成的。对于表示层,我们使用javax.faces.view.facelets.ResourceResolver实现,该实现首先尝试在使用核心资源之前在扩展 jar 中查找资源。

我们使用大量复合组件进行可重用标记。考虑用于显示地址、工资等的组件。

当涉及到应用程序的可扩展性时,Facelets引起了很大的麻烦,我开始怀疑是否有解决我们遇到的问题的方法。

我们想要实现的是,为复合组件提供一个标准接口,但通过解析多个实现以某种方式覆盖实现,其中扩展实现应该在核心实现之前。

当然,这个想法是,核心定义应用程序的标准布局/模板,扩展定义特定于客户端的格式选项,或隐藏/显示应用程序执行的托管模型的一部分。

例如:

<ui:composition 
    xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:cc="http://java.sun.com/jsf/composite"
    xmlns:ui="http://java.sun.com/jsf/facelets" 
    xmlns:pui="http://java.sun.com/jsf/composite/pui">
    <cc:interface>
        <cc:attribute name="saveButtonLabel" />
        <cc:attribute name="saveButtonIcon" />
        <cc:attribute name="saveButtonIconPosition" />
    </cc:interface>
    <cc:implementation>
        <pui:pension_plan_custom_state pensionPlanBean="#{pensionPlanBean}" />
        <pui:pension_plan_general pensionPlanBean="#{pensionPlanBean}" />
        <pui:pension_plan_pension_plan pensionPlanBean="#{pensionPlanBean}" />
        <pui:pension_plan_salary pensionPlanBean="#{pensionPlanBean}" />
        <pui:pension_plan_investments pensionPlanBean="#{pensionPlanBean}" />
        <pui:pension_plan_benefit_types benefitTypes="#{pensionPlanBean.benefitTypesViewData}" />
        <div class="buttons">
            <!-- Irrelevant -->
        </div>
    </cc:implementation>
</ui:composition>

以复合组件pension_plan_benefit_types为例。它的接口规定客户端将按名称benefitTypes提供属性。如果客户端只希望屏幕的这一部分被与标准实现不同的内容覆盖,我们需要一个地方在某处覆盖它。

另请注意,核心不知道(可选)扩展提供的命名空间。核心并不在乎,只要复合的界面稳定,不需要改变。

作为最后的手段,已经尝试了以下方法(伪代码):

<ui:composition
    xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:cc="http://java.sun.com/jsf/composite"
    xmlns:ui="http://java.sun.com/jsf/facelets" 
    xmlns:pui="http://java.sun.com/jsf/composite/pui">
    <cc:interface>
        <cc:attribute name="benefitTypes" required="true" type="com.foo.bar.PensionPlanBenefitTypesViewData" />
    </cc:interface>
    <cc:implementation>
        <ui:include src="/resources/pui/markup/pension_plan_benefit_types_markup.xhtml" />
    </cc:implementation>
</ui:composition>

我们的想法是,javax.faces.view.facelets.ResourceResolver会来拯救我们。

它"有点工作",但根据我们必须为复合组件编写的实现,我们在臭名昭著的构建中遇到了各种问题,与 JSF 生命周期的渲染时间部门相比。它基本上没有按照我们期望的方式工作。

现在最大的问题是:

有没有一种方法可以让我们既有一个稳定的合约/命名空间,又有多个动态解析的实现?

希望有人能对此有所了解,感谢您的投入。

亲切问候

仁斯

JSF 2.2 中已通过资源库协定功能解决了这种情况。使用该功能,可以根据其本地化和活动协定对同一复合组件进行多个实现。请注意,本地化/合同在每个视图都是有效的。

但可能更适合您的选项是使用:

<ui:include src="#{...}">

<ui:decorate template="#{...}">

并提供来自受管 Bean 或复合组件类本身的模板名称 (cc:interface componentType=...)。在这种情况下,我建议使用最新版本的MyFaces Core,因为它的算法已针对这些情况进行了专门优化(小视图状态大小和快速性能)。我认为您不需要在这里处理资源解析器逻辑。

我们扩展了ResourceHandlerWrapper,以便在请求加载来自WEB-INF/resources的 xhtml 资源时(首先)扫描类路径。

通过在MANIFEST.MF文件中定义显式类路径,我们可以让扩展 jar 文件优先于标准实现。通过这样做,我们也可以通过编程方式生成其他内容,这些内容可以在UI中显示给开发人员(在开发模式下运行时),让他/她知道可以在扩展项目中编写"覆盖"实现。

最新更新