解決 JSP 頁面佈局重複問題:Header、Sidebar、Footer 等公共區塊定義一次,業務頁面僅關注 Content 區塊。對比 <iframe>/<frame>:
struts2-tiles-plugin(Struts 2 內建)或手動加 tiles-api、tiles-core、tiles-jsp、tiles-servlet。web.xml 註冊 TilesListener(Struts 2 可省略,由 Filter 自動初始化)。struts.xml:<result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult"/>tiles-defs.xml 定義佈局與頁面組合。<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
"https://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
<!-- 基礎佈局模板 -->
<definition name="baseLayout" template="/WEB-INF/tiles/baseLayout.jsp">
<put-attribute name="header" value="/WEB-INF/tiles/header.jsp"/>
<put-attribute name="sidebar" value="/WEB-INF/tiles/sidebar.jsp"/>
<put-attribute name="footer" value="/WEB-INF/tiles/footer.jsp"/>
<put-attribute name="content" value=""/> <!-- 預留,子定義覆蓋 -->
</definition>
<!-- 首頁:繼承基礎佈局,僅替換 content -->
<definition name="home" extends="baseLayout">
<put-attribute name="content" value="/WEB-INF/views/home.jsp"/>
</definition>
<!-- 用戶列表:同理 -->
<definition name="userList" extends="baseLayout">
<put-attribute name="content" value="/WEB-INF/views/user/list.jsp"/>
<put-attribute name="sidebar" value="/WEB-INF/tiles/userSidebar.jsp"/> <!-- 可選覆蓋側邊欄 -->
</definition>
</tiles-definitions>
<%@ taglib prefix="tiles" uri="https://tiles.apache.org/tags-tiles" %>
<html>
<head><title><tiles:insertAttribute name="title" ignore="true"/></title></head>
<body>
<div id="header"><tiles:insertAttribute name="header"/></div>
<div id="sidebar"><tiles:insertAttribute name="sidebar"/></div>
<div id="content"><tiles:insertAttribute name="content"/></div>
<div id="footer"><tiles:insertAttribute name="footer"/></div>
</body>
</html>
@Action(value = "home", results = {
@Result(name = "success", type = "tiles", location = "home")
})
public class HomeAction extends ActionSupport {
public String execute() { return SUCCESS; }
}
或 Struts 1 風格:return mapping.findForward("home"); 對應 <forward name="home" path="home" redirect="false"/>(需配置 Tiles 為默認 result type)。
<definition name="articleDetail" extends="baseLayout">
<put-attribute name="content" value="/WEB-INF/views/article/detail.jsp"/>
<put-attribute name="articleId" value="{1}" /> <!-- 通配符從 Action mapping 獲取 -->
</definition>
Action 映射:/article/{articleId},{1} 自動綁定到 articleId 屬性,detail.jsp 可用 ${articleId} 取值。
<definition name="dashboard" extends="baseLayout">
<put-list-attribute name="widgets">
<add-attribute value="/WEB-INF/widgets/sales.jsp"/>
<add-attribute value="/WEB-INF/widgets/traffic.jsp"/>
<add-attribute value="/WEB-INF/widgets/alerts.jsp"/>
</put-list-attribute>
</definition>
模板中:<tiles:insertAttribute name="widgets"/> 自動遍歷渲染。
<definition name="dynamicPage" extends="baseLayout" preparer="com.example.TilesPreparer">
<put-attribute name="content" value="/WEB-INF/views/dynamic.jsp"/>
</definition>
public class TilesPreparer implements ViewPreparer {
public void execute(Request request, ServletContext context,
Map model, URL url, TilesContainer container) {
model.put("userMenu", menuService.buildUserMenu(request.getUserPrincipal()));
}
}
| 問題 | 原因 | 解決 |
|---|---|---|
| 屬性未生效 | 拼寫錯誤、類型不匹配(put-attribute vs put-list-attribute) | 檢查 DTD、啟用 debug 日誌 org.apache.tiles |
| 繼承鏈過深導致難維護 | 濫用 extends | 控制在 2-3 層,組合優於繼承 |
| 模板路徑 404 | 路徑需相對 WebApp 根,/WEB-INF/... 不可直接瀏覽器訪問 | 統一放 /WEB-INF/tiles/、/WEB-INF/views/ |
| Tiles 3 + Struts 2 版本衝突 | 傳遞依賴版本不一致 | 統一用 struts2-tiles-plugin 管理的版本 |
Tiles 以定義繼承 + 屬性覆蓋實現佈局復用,核心優勢在於伺服器端組裝、靈活覆蓋、支援動態屬性/準備器。新專案若使用 Spring MVC,可考慮 Thymeleaf Layout Dialect 或 Tiles 3 獨立整合(無需 Struts)。