【问题标题】:Can I nest a composite component in itself recursively?我可以递归地嵌套复合组件吗?
【发布时间】:2012-01-16 19:15:49
【问题描述】:

我有包含自身的 DTO(它是嵌套的)。

public class MyDTO {

   private SomeData someData;
   private MyDTO nested;

   // getters and setters
}

我创建了复合组件,它以递归方式调用自身。我这样称呼它:

<screen:my-dto-screen dto="#{myDTOBean.myDto}" />

定义是:

  <composite:interface>
    <composite:attribute name="dto"/>
</composite:interface>
        -- display "someData" here --
    <p:panel rendered="#{cc.attrs.dto.nested != null}" /> -- this acts as recursion bottom --
       <screen:my-dto-screen dto="#{cc.attrs.dto.nested}" />            
    </p:panel>
<composite:implementation>

真正的代码并不是这么简单,但我写这个是为了让你简单地了解问题所在。一切似乎都很好,但我看到了一些奇怪的异常。

javax.servlet.ServletException
javax.faces.webapp.FacesServlet.service(FacesServlet.java:422)
org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:79)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:368)
cayetano.games.common.web.security.OperatorPermissionsFilter.doFilter(OperatorPermissionsFilter.java:74)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:97)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:100)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:78)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:35)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
cayetano.games.common.web.security.WebUsernamePasswordAuthenticationFilter.actualDoFilter(WebUsernamePasswordAuthenticationFilter.java:65)
cayetano.games.common.web.security.WebUsernamePasswordAuthenticationFilter.doFilter(WebUsernamePasswordAuthenticationFilter.java:43)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:169)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)

root cause

java.lang.StackOverflowError
java.util.regex.Pattern$Curly.match(Pattern.java:3754)
java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3383)
java.util.regex.Pattern$Slice.match(Pattern.java:3499)
java.util.regex.Pattern$GroupTail.match(Pattern.java:4244)
java.util.regex.Pattern$BranchConn.match(Pattern.java:4095)
java.util.regex.Pattern$BmpCharProperty.match(Pattern.java:3383)
java.util.regex.Pattern$Branch.match(Pattern.java:4131)
java.util.regex.Pattern$GroupHead.match(Pattern.java:4185)
java.util.regex.Pattern$CharProperty.match(Pattern.java:3362)
java.util.regex.Pattern$Start.match(Pattern.java:3072)
java.util.regex.Matcher.search(Matcher.java:1116)
java.util.regex.Matcher.find(Matcher.java:552)
com.sun.faces.el.ELUtils.isCompositeComponentExpr(ELUtils.java:195)
com.sun.faces.facelets.tag.TagAttributeImpl.getValueExpression(TagAttributeImpl.java:388)
com.sun.faces.facelets.tag.TagAttributeImpl.getValueExpression(TagAttributeImpl.java:351)
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler$CompositeComponentRule$CompositeExpressionMetadata.applyMetadata(CompositeComponentTagHandler.java:588)
com.sun.faces.facelets.tag.MetadataImpl.applyMetadata(MetadataImpl.java:81)
javax.faces.view.facelets.MetaTagHandler.setAttributes(MetaTagHandler.java:129)
javax.faces.view.facelets.DelegatingMetaTagHandler.setAttributes(DelegatingMetaTagHandler.java:102)
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.setAttributes(CompositeComponentTagHandler.java:226)
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyNextHandler(CompositeComponentTagHandler.java:183)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
com.sun.faces.facelets.tag.composite.ImplementationHandler.apply(ImplementationHandler.java:81)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93)
com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:86)
com.sun.faces.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:152)
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyCompositeComponent(CompositeComponentTagHandler.java:349)
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyNextHandler(CompositeComponentTagHandler.java:190)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
com.sun.faces.facelets.tag.composite.ImplementationHandler.apply(ImplementationHandler.java:81)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93)
com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:86)
com.sun.faces.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:152)
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyCompositeComponent(CompositeComponentTagHandler.java:349)
com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.applyNextHandler(CompositeComponentTagHandler.java:190)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
javax.faces.view.facelets.DelegatingMetaTagHandler.applyNextHandler(DelegatingMetaTagHandler.java:137)
com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:188)
javax.faces.view.facelets.DelegatingMetaTagHandler.apply(DelegatingMetaTagHandler.java:120)
com.sun.faces.facelets.tag.composite.ImplementationHandler.apply(ImplementationHandler.java:81)

【问题讨论】:

    标签: jsf recursion jsf-2 primefaces composite-component


    【解决方案1】:

    此异常发生在视图构建期间。 rendered 属性在视图构建时评估。它仅在视图渲染期间进行评估(以及在输入和命令组件的应用请求值阶段,但这是另一回事)。因此,您的方法最终会陷入无限递归循环,最终导致内存中的堆栈溢出。

    基本上,您希望在视图构建期间评估条件。因此,您需要一个在视图构建期间运行的标签/属性。其中之一是 JSTL &lt;c:if&gt;

    <c:if test="#{cc.attrs.dto.nested != null}">
        <p:panel>
           <screen:my-dto-screen dto="#{cc.attrs.dto.nested}" />            
        </p:panel>
    </c:if>
    

    但是,如果 #{cc.attrs.dto.nested} 值由渲染时间属性提供,则此方法不起作用,因此递归将不起作用。只会显示顶级组件。您正在寻找一种迭代方法。考虑查看现有的 JSF 树组件,例如 PrimeFaces 的 &lt;p:tree&gt;

    【讨论】:

    • 感谢您的帮助,但 标签
    【解决方案2】:

    我遇到了与 OP 类似的问题,并设法通过 foreach 和 BalusC 提供的以下博客文章解决了这个问题。以下所有内容均引用自此来源,并归功于 BalusC:

    Recursive tree of composite components

    正如 BalusC 已经提到的,使用在视图构建期间运行的标签/属性非常重要,例如 c:if 和 c:foreach。但是,下面的示例代码仍会产生 stackoverflowerror

    <cc:interface>
        <cc:attribute name="node" type="com.example.SomeTreeModel" />
    </cc:interface>
    <cc:implementation>
        <c:if test="#{not empty cc.attrs.node.children}">
            <ul>
                <c:forEach items="#{cc.attrs.node.children}" var="node">
                    <li>
                        #{node.data}
                        <my:tree node="#{node}" />
                    </li>
                </c:forEach>
            </ul>
        </c:if>
    </cc:implementation>
    

    结果是,当您将 #{node} 传递给嵌套组合时,从技术上讲,您基本上是在将 #{cc.attrs.node.children[index]} 传递给它。嵌套组合反过来将其 #{cc.attrs.node} 解释为 #{cc.attrs[cc.attrs.node.children[index]]}。但是......在嵌套组合的上下文中,#{cc} 指的是嵌套组合本身!所以,#{cc.attrs.node} 实际上是指嵌套组合自己的节点。这会导致一个永无止境的循环。

    为了避免我们想要的stackoverflowerror:

    1. 引用父组件

    2. 组合在内部立即评估“所需变量”并将其作为基于请求的实例变量存储在某处,而不是仅将 ValueExpression 对象存储在组件状态中。支持组件是一个很好的候选。

    @FacesComponent("treeComposite")
    public class TreeComposite extends UINamingContainer {
    
        private SomeTreeModel node;
    
        @Override
        public void setValueExpression(String name, ValueExpression expression) {
            if ("node".equals(name)) {
                setNode((SomeTreeModel) expression.getValue(getFacesContext().getELContext()));
            }
            else {
                super.setValueExpression(name, expression);
            }
        }
    
        public SomeTreeModel getNode() {
            return node;
        }
    
        public void setNode(SomeTreeModel node) {
            this.node = node;
        }
    
    }
    

    和组件

    <cc:interface componentType="treeComposite">
        <cc:attribute name="node" type="com.example.SomeTreeModel" />
    </cc:interface>
    <cc:implementation>
        <c:if test="#{not empty cc.node.children}">
            <ul>
                <c:forEach items="#{cc.node.children}" var="node" varStatus="loop">
                    <li>
                        #{node.data}
                        <my:tree node="#{cc.parent.node.children[loop.index]}" />
                    </li>
                </c:forEach>
            </ul>
        </c:if>
    </cc:implementation>
    

    【讨论】:

    • @Kukeltje 根据您提供的链接中的共识,我已经编辑了我的答案以包含博客文章中最重要的部分。因此,希望我已经反驳了链接丢失的问题:)
    猜你喜欢
    • 2022-12-18
    • 2016-02-16
    • 2019-04-18
    • 2017-03-18
    • 2021-05-01
    • 1970-01-01
    • 2015-06-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多