问题描述
当我尝试将复合组件嵌套在其内部时,通过一些逻辑来结束无限递归,我收到了堆栈溢出异常.我的理解是 <c:xxx>
标签在视图构建时运行,因此我不希望像我这样拥有无限的视图构建假定是这种情况.
When I attempt to nest a Composite Component within itself, with some logic to end the infinite recursion I receive a stack overflow exception. My understanding is that <c:xxx>
tags run at view build time so I was not expecting to have an infinite view build as I presume has been the case.
这是复合组件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)
问题
如何在不接收堆栈溢出异常的情况下将复合组件(或类似组件)嵌套在其内部(达到未预定义的深度)
The Question
How can I nest composite components (or similar) within themselves (to a non predefined depth) without receiving a stack overflow exception
我想在RichFaces的嵌套collapsibleSubTable中表示任意嵌套的数据,非常欢迎使用我的方法的替代方法
I have arbitrarily nested data that I want to represent within a nested collapsibleSubTable from RichFaces, alternatives to my approach are very welcome
推荐答案
问题出在#{cc}
和复合属性的有状态性的上下文中.嵌套组合的任何属性中的#{cc}
引用自身而不是父级.该属性为有状态属性意味着#{cc}
在每个子代中都被重新评估,而该子代最终引用了自身而不是父代.因此,堆栈溢出.它正在无限循环地评估自身的深度.
The problem was in the context of the #{cc}
and the statefulness of the composite attribute. The #{cc}
in any attribute of the nested composite references itself instead of the parent. The attribute being stateful means that the #{cc}
was re-evaluated in every child which in turn ultimately references itself instead of the parent. Hence the stack overflow. It's evaluating the depth of itself in an infinite loop.
我通过使用以下支持组件使属性变为无状态,从而欺骗了该属性的有状态性,该组件立即对其进行评估并将其分配为组件属性:
I tricked the statefulness of the attribute by making it stateless using a backing component as below which immediately evaluates it and assigns it as a component property:
@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
中声明如下:
Which is to be declared in interface's componentType
as below:
<cc:interface componentType="treeComposite">
<cc:attribute name="depth" type="java.lang.Integer" />
</cc:interface>
而且,在实现中,您应该在测试中引用无状态属性,并在嵌套复合引用中引用父级之一(因为嵌套复合属性的#{cc}
引用嵌套复合本身):
And, in the implementation you should in the test reference the stateless property and in the nested composite reference the one of the parent (because #{cc}
in the attribute of the nested composite references the nested composite itself):
<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}"
:
I only changed the meaning of "depth" here to be the other way round so that it's just declarative from the client on without the need to edit it in the implementation. So, in the client you have to say depth="#{3}"
if you want 3 nested children:
<my:tree depth="#{3}" />
请注意,它是EL表达式而不是文字的重要性.否则,将不会调用支持组件中的setValueExpression()
.
Note the importance of it being an EL expression rather than a literal. Otherwise setValueExpression()
in the backing component won't be called.
这篇关于嵌套的JSF Composite组件导致堆栈溢出异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!