You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从Struts1/Tiles1迁移到Struts2/Tiles3触发StackOverflowError

Struts 1.3/Tiles 1.x 迁移至 Struts 2/Tiles 3 菜单改造后出现StackOverflowError

背景

从Struts 1.3/Tiles 1.x组合迁移至Struts 2/Tiles 3,由于Tiles 3不再支持在定义中声明bean实现菜单项功能,因此对相关配置及页面进行改造,改造后运行触发java.lang.StackOverflowError,寻求解决方案。

原实现代码

原Tiles定义

<definition name=".selectPatient.menu" path="/WEB-INF/templates/menu.jsp">
    <putList name="items">
        <bean classtype="org.a.m.s.jsp.MenuItem">
            <set-property property="id" value="menubar_register"/>        
            <set-property property="name" value="menubar_register"/>        
            <set-property property="title" value="Register"/>        
            <set-property property="onclick" value="registerPatient()"/>        
            <set-property property="text" value="Register"/>
            <set-property property="roles" value="BASIC,LEAD,SUPER"/>        
        </bean>
        <bean classtype="org.a.m.s.jsp.MenuItem">
            <set-property property="id" value="menubar_multitransaction"/>        
            <set-property property="name" value="menubar_multitransaction"/>        
            <set-property property="title" value="Post Multiple Transactions"/>        
            <set-property property="onclick" value="postMultiTransactions()"/>        
            <set-property property="text" value="Post Multiple Transactions"/>
            <set-property property="roles" value="BASIC,LEAD,SUPER"/>
        </bean>
    </putList>
</definition> 

原menu.jsp

<logic:iterate id="item" name="items" type="org.a.m.s.jsp.MenuItem">
    <%if (UserRoles.userHasRole(request,item.getRoles())){%><td><table id="button_<bean:write name="item" property="id"/>" cellpadding="0" cellspacing="0" border="0"><tr><td><img src="images/menu_button_round_edge_left2.gif"></td>
        <td style="border-top:1px white solid; border-bottom:1px white solid;">
            <a name="<bean:write name="item" property="name"/>" id="<bean:write name="item" property="id"/>" accesskey="M" title="<bean:write name="item" property="title"/>" class="menuItem" href="#" isDisabled="false" onclick="if (this.isDisabled != 'true'&&isPageReady()) <bean:write name="item" property="onclick"/>;" 
            onmouseover="if (this.isDisabled != 'true'&&isPageReady())document.getElementById('button_<bean:write name="item" property="id"/>').style.backgroundColor='#ffff99';" onmouseout="if (this.isDisabled != 'true'&&isPageReady())document.getElementById('button_<bean:write name="item" property="id"/>').style.backgroundColor='#000066';">"<bean:write name="item" property="text"/></a></td>
        <td><img src="images/menu_button_round_edge_right2.gif" width="9" height="17"></td>
    </tr></table></td>
    <%}%>
</logic:iterate>

改造后代码

新Tiles定义

<definition name="defaultMenuComponent" template="/WEB-INF/templates/barbutton.jsp">
</definition>

<definition name="menubar_register" extends="defaultMenuComponent">
  <put-attribute name="id" value="menubar_register" cascade="true"/>
  <put-attribute name="name" value="menubar_register" cascade="true"/>
  <put-attribute name="title" value="Register" cascade="true"/>
  <put-attribute name="onclick" value="registerPatient()" cascade="true"/> 
  <put-attribute name="text" value="Register" cascade="true"/>
  <put-attribute name="roles" value="BASIC,LEAD,SUPER" cascade="true"/>   
</definition>

<definition name="menubar_multitransaction" extends="defaultMenuComponent">
  <put-attribute name="id" value="menubar_multitransaction" cascade="true"/>
  <put-attribute name="name" value="menubar_multitransaction" cascade="true"/>
  <put-attribute name="title" value="Post Multiple Transactions" cascade="true"/>
  <put-attribute name="onclick" value="postMultiTransactions()" cascade="true"/> 
  <put-attribute name="text" value="Post Multiple Transactions" cascade="true"/>
  <put-attribute name="roles" value="BASIC,LEAD,SUPER" cascade="true"/>   
</definition>

<definition name=".selectPatient.menu" template="/WEB-INF/templates/menu.jsp">
    <put-list-attribute name="items">
       <add-attribute value="menubar_register" type="definition"/>
       <add-attribute value="menubar_multitransaction" type="definition"/>   
    </put-list-attribute>
</definition>

barbutton.jsp

<table id="button_<tiles:insertAttribute name="id" flush="true"/>" cellpadding="0" cellspacing="0" border="0">
<tr>
<td><img src="images/menu_button_round_edge_left2.gif"></td>
<td style="border-top:1px white solid; border-bottom:1px white solid;">
<a name="<tiles:insertAttribute name="name" flush="true"/>" id="<tiles:insertAttribute name="id" flush="true"/>" accesskey="M" title="<tiles:insertAttribute name="title" flush="true"/>" class="menuItem" href="#" isDisabled="false" onclick="if (this.isDisabled != 'true'&&isPageReady()) <tiles:insertAttribute name="onclick" flush="true"/>;" onmouseover="if (this.isDisabled != 'true'&&isPageReady())document.getElementById('button_<tiles:insertAttribute name="id" flush="true"/>').style.backgroundColor='#ffff99';" onmouseout="if (this.isDisabled != 'true'&&isPageReady())document.getElementById('button_<tiles:insertAttribute name="id" flush="true"/>').style.backgroundColor='#000066';">"<tiles:insertAttribute name="text" flush="true"/>
</a>
</td>
<td><img src="images/menu_button_round_edge_right2.gif" width="9" height="17"></td>
</tr>
</table>

修改后的menu.jsp

<tilesx:useAttribute id="items" name="items" classname="java.util.List"/>

  <table border=0 cellpadding=0 cellspacing=1>
  <tr>       
     <c:forEach items="${items}" var="listItem">
       <td> 
       <tiles:insertDefinition name="${listItem}" flush="true"/> 
       </td>
     </c:forEach>
  </tr>
  </table>

问题分析

出现StackOverflowError的核心原因是循环引用:

  • 在.selectPatient.menu定义中,将menubar_register等定义添加到items列表中
  • 当menu.jsp通过<tiles:insertDefinition>渲染这些子定义时,若子定义存在间接/直接引用父定义的配置,或Tiles解析定义时出现递归逻辑,就会触发无限递归调用,最终导致栈溢出。

另外,改造后丢失了原代码中的角色校验逻辑,这部分功能也需要补回。

解决方案

1. 修复循环引用,调整Tiles定义结构

将子定义从独立可插入的Tiles定义改为属性组,避免递归解析:

<definition name=".selectPatient.menu" template="/WEB-INF/templates/menu.jsp">
    <put-list-attribute name="items">
       <add-attribute>
           <put-attribute name="id" value="menubar_register"/>
           <put-attribute name="name" value="menubar_register"/>
           <put-attribute name="title" value="Register"/>
           <put-attribute name="onclick" value="registerPatient()"/> 
           <put-attribute name="text" value="Register"/>
           <put-attribute name="roles" value="BASIC,LEAD,SUPER"/>   
       </add-attribute>
       <add-attribute>
           <put-attribute name="id" value="menubar_multitransaction"/>
           <put-attribute name="name" value="menubar_multitransaction"/>
           <put-attribute name="title" value="Post Multiple Transactions"/>
           <put-attribute name="onclick" value="postMultiTransactions()"/> 
           <put-attribute name="text" value="Post Multiple Transactions"/>
           <put-attribute name="roles" value="BASIC,LEAD,SUPER"/>   
       </add-attribute>
    </put-list-attribute>
</definition>

2. 修复menu.jsp,补全角色校验逻辑

修改后的menu.jsp直接渲染属性,无需调用insertDefinition:

<tilesx:useAttribute id="items" name="items" classname="java.util.List"/>

<table border=0 cellpadding=0 cellspacing=1>
<tr>       
   <c:forEach items="${items}" var="menuItem">
       <% 
           String roles = (String) ((Map)pageContext.getAttribute("menuItem")).get("roles");
           if (UserRoles.userHasRole(request, roles)) {
       %>
       <td> 
           <table id="button_${menuItem.id}" cellpadding="0" cellspacing="0" border="0">
           <tr>
           <td><img src="images/menu_button_round_edge_left2.gif"></td>
           <td style="border-top:1px white solid; border-bottom:1px white solid;">
           <a name="${menuItem.name}" id="${menuItem.id}" accesskey="M" title="${menuItem.title}" class="menuItem" href="#" isDisabled="false" onclick="if (this.isDisabled != 'true'&&isPageReady()) ${menuItem.onclick};" 
               onmouseover="if (this.isDisabled != 'true'&&isPageReady())document.getElementById('button_${menuItem.id}').style.backgroundColor='#ffff99';" 
               onmouseout="if (this.isDisabled != 'true'&&isPageReady())document.getElementById('button_${menuItem.id}').style.backgroundColor='#000066';">${menuItem.text}
           </a>
           </td>
           <td><img src="images/menu_button_round_edge_right2.gif" width="9" height="17"></td>
           </tr>
           </table>
       </td>
       <% } %>
   </c:forEach>
</tr>
</table>

3. 清理冗余配置

删除defaultMenuComponent、menubar_register等独立定义,避免配置冗余和潜在的循环风险。

4. 排查递归根源(可选)

若保留原定义结构,需查看栈溢出的详细堆栈信息,定位具体哪个定义引发递归;同时检查cascade="true"属性是否导致属性反向传递,触发循环引用。

内容的提问来源于stack exchange,提问作者Arbuz Melon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 17:17:02