JSF ui:fragment 渲染性能

2022-01-21 00:00:00 performance components java jsf

我有一组 jsf 组件,它们是从一组 excel 文件中静态生成的(它们由业务人员更新).每个生成的文件代表一个业务对象,其数据略有不同,并且都属于同一个类.

I have a set of jsf components that are statically generated from a set of excel files (they are updated by business people). Each generated file represents a business object that has slightly different data, and all of them belong to a same class.

为了动态渲染,我找到的唯一解决方案是设置一堆 ui:fragment 并在运行时调度到正确的组件:

In order to render this dynamically, the only solution I found was to set up a bunch of ui:fragment and dispatch to the right component at runtime:

<!-- IMPLEMENTATION -->          
<composite:implementation> 
    <ui:fragment rendered="#{cc.attrs.type eq 'cartcred'}">
        <limites:limites-cartcred  limite="#{cc.attrs.limite}"/>
    </ui:fragment>
    <ui:fragment rendered="#{cc.attrs.type eq 'cdcp'}">
        <limites:limites-cdcp limite="#{cc.attrs.limite}"/>
    </ui:fragment>
    <ui:fragment rendered="#{cc.attrs.type eq 'cheqpredatado'}">
        <limites:limites-cheqpredatado limite="#{cc.attrs.limite}"/>
    </ui:fragment>
    <ui:fragment rendered="#{cc.attrs.type eq 'confirming'}">
        <limites:limites-confirming limite="#{cc.attrs.limite}"/>
    </ui:fragment>
   <!-- many more lines -->
   <!-- many more lines -->
   <!-- many more lines -->
    <ui:fragment rendered="#{cc.attrs.type eq 'contacorr'}">
        <limites:limites-contacorr limite="#{cc.attrs.limite}"/>
    </ui:fragment>

但我发现它的性能很糟糕.我坚持认为 JSF 只会渲染单个组件,但似乎它正在渲染所有它们并在运行时隐藏"其他组件.

But I found out that the perfomance of this is terrible. I tought that JSF would only render a single component, but it seems that it is rendering all of them and "hiding" the others at runtime.

有没有更有效的方法来实现我的目标?我想根据有关业务类的运行时信息(很像 if-then-else)呈现单个组件,但我只能确定要在运行时呈现的组件是什么.

Is there a more efficient way of achieving my goal? I want to render a single component based on runtime information about a business class (much like an if-then-else), but I can only determine what is the component to render at runtime.

说明:发生的情况是 limites:limites* 引用的每个组件都是一个巨大的复杂页面,其中包含许多其他组件.在运行时,名为 type' 的参数将决定要渲染的组件.但是我的测试表明,如果我只渲染一个组件,而留下另一个ui:fragments`(即使知道它们不会被渲染),它的渲染速度会比我慢得多移除组件.

Clarification: what happens is that each component referenced by limites:limites* is a huge complex page with lots of other components. At runtime, the parameter named type' will decide what component to render. But my tests show that if I only render one component, but leave the otherui:fragments` (even knowing that they will not be rendered), it will render much slower than if I remove the components.

所以如果我的页面是这样的:

So if my page is exactly like this:

<composite:interface>
    <composite:attribute name="type" required="true" />
    <composite:attribute name="limite" required="true" />
</composite:interface>         
<composite:implementation> 
    <ui:fragment rendered="#{cc.attrs.type eq 'cartcred'}">
        <limites:limites-cartcred  limite="#{cc.attrs.limite}"/>
    </ui:fragment>
</composite:implementation>

即使参数相同,它的渲染速度也会比初始版本快很多(大约 10 倍).我怀疑 JSF 会创建整个组件树,并且只有在运行时才会决定(取决于提供的参数)是否会相互渲染.

it will render much (around 10x) faster than the initial version, even though the parameters are the same. I suspect that JSF will create the entire component tree and only at runtime it will decide (depending on the supplied parameter) if it will render each other or not.

编辑

差不多了.我只需要包含我的复合组件 动态.我尝试评估一个 ELExpression 但没有奏效.我需要的是一种在组件创建中访问当前范围的方法,并使用它来生成正确的文件名:

Almost there. I just need to include my composite component dynamically. I tried evaluating an ELExpression but that didn't work. What I need is a way of accessing the current scope within the component creation, and using that to generate the proper file name:

//obviously, ELExpressions don't work here
Resource resource = application.getResourceHandler().createResource("file-#{varStatus.loop}.xhtml", "components/dynamicfaces");

推荐答案

一种可能是使用 binding 属性来访问容器从托管 bean 内部构建组件树,并从java端.这样你就可以只包含需要的组件,不需要组件根本不会被评估.

One possibility might be to use the binding attribute to access a container component from inside your managed bean and build the component tree from the java side. That way you could include only the needed components, unneeded components won't be evaluated at all.

JSP:

<h:panelGroup binding="#{managedBean.panel}"/>

托管 Bean:

private UIPanel panel;

// getter and setter


// Action method, might also work in a @PostConstruct
public String showComponent() {
    if (showComponent1) {
        UIOutput component1 = new HtmlOutputText();
        component1.setValue("Hello world!");

        getPanel().getChildren().add(component1);
    }

    return "viewId";
}

我还没有将它与复合组件一起使用,this question 似乎有更多细节和 示例应用程序关于将其与复合组件一起使用.

I haven't used this together with composite components yet, this question seems to have some more details and an example application regarding using this with composite components.

关于您的编辑,您还可以像这样评估托管 bean 中的 EL 表达式:

Regarding your edit, you can also evaluate EL expressions in your managed bean like this:

FacesContext facesContext = FacesContext.getCurrentInstance();
ELContext elContext = facesContext.getELContext();
ExpressionFactory exprFactory = facesContext.getApplication().getExpressionFactory();
ValueExpression expr = exprFactory.createValueExpression(elContext, "#{expr}", String.class);
String value = (String) expr.getValue(elContext);

相关文章