【问题标题】:Nested JSF Composite Components leading to a Stack Overflow exception导致堆栈溢出异常的嵌套 JSF 复合组件
【发布时间】:2015-07-11 07:06:28
【问题描述】:

问题

当我尝试将复合组件嵌套在自身内部时,使用一些逻辑来结束无限递归,我收到堆栈溢出异常。我的理解是<c:xxx> tags run at view build time,所以我没想到会有无限的视图构建,因为我认为是这种情况。

这是复合组件simpleNestable.xhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:composite="http://java.sun.com/jsf/composite"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:em="http://xmlns.jcp.org/jsf/composite/emcomp"

  xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">

    <h:head>
        <title>This content will not be displayed</title>
    </h:head>
    <h:body>
        <composite:interface>
            <composite:attribute name="depth" required="true" type="java.lang.Integer"/>
        </composite:interface>

        <composite:implementation>
            <c:if test="#{cc.attrs.depth lt 3}">
                 #{cc.attrs.depth}
                 #{cc.attrs.depth+1}
                 <em:simpleNestable depth="#{cc.attrs.depth+1}" /> 

            </c:if>

        </composite:implementation>
    </h:body>
</html>

这就是它的使用方式

<h:head>
    <title>Facelet Title</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <h:outputStylesheet name="./css/default.css"/>
    <h:outputStylesheet name="./css/cssLayout.css"/>
</h:head>
<h:body>        
     <emcomp:simpleNestable depth="1"/>

</h:body>

堆栈溢出异常

java.lang.StackOverflowError
    at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:109)
    at javax.faces.component.UIComponentBase$AttributesMap.get(UIComponentBase.java:2407)
    at com.sun.faces.el.CompositeComponentAttributesELResolver$ExpressionEvalMap.get(CompositeComponentAttributesELResolver.java:393)
    at javax.el.MapELResolver.getValue(MapELResolver.java:199)
    at com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:176)
    at com.sun.faces.el.DemuxCompositeELResolver.getValue(DemuxCompositeELResolver.java:203)
    at com.sun.el.parser.AstValue.getValue(AstValue.java:140)
    at com.sun.el.parser.AstValue.getValue(AstValue.java:204)
    at com.sun.el.parser.AstPlus.getValue(AstPlus.java:60)
    at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:226)
    at org.jboss.weld.el.WeldValueExpression.getValue(WeldValueExpression.java:50)
    at com.sun.faces.facelets.el.ContextualCompositeValueExpression.getValue(ContextualCompositeValueExpression.java:158)
    at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:109)
    at javax.faces.component.UIComponentBase$AttributesMap.get(UIComponentBase.java:2407)
    at com.sun.faces.el.CompositeComponentAttributesELResolver$ExpressionEvalMap.get(CompositeComponentAttributesELResolver.java:393)
    at javax.el.MapELResolver.getValue(MapELResolver.java:199)
    at com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:176)
    at com.sun.faces.el.DemuxCompositeELResolver.getValue(DemuxCompositeELResolver.java:203)
    at com.sun.el.parser.AstValue.getValue(AstValue.java:140)
    at com.sun.el.parser.AstValue.getValue(AstValue.java:204)
    at com.sun.el.parser.AstPlus.getValue(AstPlus.java:60)

问题

如何在不接收堆栈溢出异常的情况下将复合组件(或类似组件)嵌套在自身内部(到非预定义深度)

为什么我想要这个

我有任意嵌套的数据,我想在 RichFaces 的嵌套 collapsibleSubTable 中表示,非常欢迎替代我的方法

【问题讨论】:

    标签: jsf jstl stack-overflow composite-component nested


    【解决方案1】:

    问题在于#{cc} 的上下文和复合属性的状态。嵌套复合的任何属性中的#{cc} 引用自身 而不是父级。有状态的属性意味着 #{cc} 在每个孩子中被重新评估,最终最终引用 自身 而不是父母。因此堆栈溢出。它在无限循环中评估自身的深度。

    我通过使用下面的支持组件使其无状态来欺骗属性的状态性,该支持组件立即评估它并将其分配为组件属性:

    @FacesComponent("treeComposite")
    public class TreeComposite extends UINamingContainer {
    
        private Integer depth;
    
        @Override
        public void setValueExpression(String name, ValueExpression binding) {
            if ("depth".equals(name)) {
                setDepth((Integer) binding.getValue(getFacesContext().getELContext()));
            }
            else {
                super.setValueExpression(name, binding);
            }
        }
    
        public Integer getDepth() {
            return depth;
        }
    
        public void setDepth(Integer depth) {
            this.depth = depth;
        }
    
    }
    

    在接口的componentType中声明如下:

    <cc:interface componentType="treeComposite">
        <cc:attribute name="depth" type="java.lang.Integer" />
    </cc:interface>
    

    而且,在实现中,您应该在测试中引用无状态属性,并在嵌套复合引用中引用父级之一(因为嵌套复合的属性中的 #{cc} 引用嵌套复合本身):

    <cc:implementation>
        <br />We're at depth #{cc.depth}.
        <c:if test="#{cc.depth gt 0}">
            <my:tree depth="#{cc.parent.depth - 1}" />
        </c:if>
    </cc:implementation>
    

    我只是在这里将“深度”的含义更改为相反的方式,以便它只是来自客户端的声明性,而无需在实现中对其进行编辑。所以,如果你想要 3 个嵌套的孩子,你必须在客户端说 depth="#{3}"

    <my:tree depth="#{3}" />
    

    请注意它是 EL 表达式而不是文字的重要性。否则不会调用支持组件中的setValueExpression()

    【讨论】:

    • 它声称“以下属性是必需的,但没有为它们提供值:深度。”我可以查看您的整个 以了解您如何定义属性深度吗?
    • 也只是为了检查:你的ccmyxmlns:cc="http://xmlns.jcp.org/jsf/compositexmlns:my="http://xmlns.jcp.org/jsf/composite/emcomp
    • 界面的深度属性与您的完全一样。命名空间确实是这样的。我只是将它们规范化(以防止未来的读者有同样的问题认为这是“提倡”的风格..“但是 BalusC 就是这样使用它的!”等等:/)。
    • @BalusC 您的博文balusc.omnifaces.org/2016/02/… 也更深入地讨论了这个话题,对吧?如果我没记错的话,你可以在上面添加一个链接。
    • @BalusC - 这在未来的 JSF 版本中是否有可能成为问题?
    猜你喜欢
    • 2019-07-02
    • 1970-01-01
    • 2010-11-27
    • 2011-01-13
    • 1970-01-01
    • 2020-11-14
    相关资源
    最近更新 更多