导致堆栈溢出异常的嵌套 JSF 复合组件
Nested JSF Composite Components leading to a Stack Overflow exception
问题
当我尝试在自身内部嵌套复合组件时,使用一些逻辑来结束无限递归,我收到堆栈溢出异常。我的理解是 <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.WhosebugError
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 中表示,非常欢迎替代我的方法
问题出在 #{cc}
的上下文和复合属性的状态中。嵌套复合引用的任何属性中的 #{cc}
本身 而不是 parent。有状态的属性意味着 #{cc}
在每个 child 中都是 re-evaluated,这反过来最终引用 自身 而不是 parent。因此堆栈溢出。它在无限循环中评估自身的深度。
我通过使用下面的支持组件使其无状态来欺骗属性的状态性,该支持组件立即对其进行评估并将其分配为组件属性:
@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>
并且,在实现中,您应该在测试中引用无状态 属性,在嵌套复合引用中引用 parent 之一(因为 #{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>
我只是将 "depth" 的含义更改为相反的意思,这样它只是来自客户端的声明,无需在实现中对其进行编辑。所以,在客户端你必须说 depth="#{3}"
如果你想要 3 嵌套 children:
<my:tree depth="#{3}" />
请注意它是 EL 表达式而不是文字的重要性。否则将不会调用支持组件中的 setValueExpression()
。
问题
当我尝试在自身内部嵌套复合组件时,使用一些逻辑来结束无限递归,我收到堆栈溢出异常。我的理解是 <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.WhosebugError
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 中表示,非常欢迎替代我的方法
问题出在 #{cc}
的上下文和复合属性的状态中。嵌套复合引用的任何属性中的 #{cc}
本身 而不是 parent。有状态的属性意味着 #{cc}
在每个 child 中都是 re-evaluated,这反过来最终引用 自身 而不是 parent。因此堆栈溢出。它在无限循环中评估自身的深度。
我通过使用下面的支持组件使其无状态来欺骗属性的状态性,该支持组件立即对其进行评估并将其分配为组件属性:
@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>
并且,在实现中,您应该在测试中引用无状态 属性,在嵌套复合引用中引用 parent 之一(因为 #{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>
我只是将 "depth" 的含义更改为相反的意思,这样它只是来自客户端的声明,无需在实现中对其进行编辑。所以,在客户端你必须说 depth="#{3}"
如果你想要 3 嵌套 children:
<my:tree depth="#{3}" />
请注意它是 EL 表达式而不是文字的重要性。否则将不会调用支持组件中的 setValueExpression()
。