本文為「Apache Geronimo 與 Spring 框架」系列第 6 部分(最終回),展示 Spring MVC 內建視圖技術的整合與切換,以電話簿應用為例。
| 技術 | 定位 | 優勢 | 適用場景 |
|---|---|---|---|
| JSP | 標準視圖 | 生態成熟、IDE 支援好、標籤庫豐富 | 傳統團隊、簡單頁面 |
| Tiles | 版面管理 | 模板繼承、片段復用、集中式版面定義 | 跨頁面共用頁首/頁尾/選單 |
| Velocity | 模板引擎 | 語法簡潔、嚴格分離 Java 邏輯與 HTML、設計師友善 | 前後端分離、需高維護性視圖 |
| PDF (iText) | 文件導出 | 程式化產出精美 PDF、Spring 封裝簡化 API | 報表、發票、列印版面 |
<jsp-config>
<taglib>
<taglib-uri>/tiles</taglib-uri>
<taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
</taglib>
</jsp-config>
phonebook-layout.jsp<%@ taglib prefix="tiles" uri="/tiles" %>
<tiles:insert name="header"/>
<tiles:insert name="body"/>
<tiles:insert name="footer"/>
TilesConfigurer 與 ResourceBundleViewResolver<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles.TilesConfigurer">
<property name="definitions">
<list><value>/WEB-INF/defs/phonebook-definitions.xml</value></list>
</property>
</bean>
<bean id="tilesViewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="order" value="1"/>
<property name="basename" value="views-phonebook-tiles"/>
</bean>
phonebook-definitions.xml<tiles-definitions>
<definition name="template" page="/WEB-INF/jsp/phonebook-layout.jsp">
<put name="header" value="/WEB-INF/jsp/header.jsp"/>
<put name="footer" value="/WEB-INF/jsp/footer.jsp"/>
</definition>
<definition name="home-mvc" extends="template">
<put name="body" value="/WEB-INF/jsp/home-mvc.jsp" type="page"/>
</definition>
<!-- addentry-mvc, modifyentry-mvc 同理 -->
</tiles-definitions>
views-phonebook-tiles.propertieshome-mvc.class=org.springframework.web.servlet.view.tiles.TilesView
home-mvc.url=home-mvc
public class PhonebookVelocityController implements Controller {
public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) {
ModelAndView mv = new ModelAndView("phonebook-template"); // 對應 phonebook-template.vm
mv.addObject("viewId", "home");
mv.addObject("time", new Date());
mv.addObject("phEntries", phonebookDao.getEntries());
return mv;
}
}
phonebook-template.vm#set ($viewName = "${viewId}.vm")
#parse("header.vm")
#parse($viewName)
#parse("footer.vm")
home.vm(片段)#foreach( $pbEntry in $phonebook )
<tr>
<td>$pbEntry.firstName $pbEntry.lastName</td>
<td>$pbEntry.homeNumber</td>
...
</tr>
#end
VelocityViewResolver<bean id="velocityViewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="suffix" value=".vm"/>
<property name="prefix" value=""/>
</bean>
*.vel<servlet-mapping>
<servlet-name>phonebook</servlet-name>
<url-pattern>*.vel</url-pattern>
</servlet-mapping>
public class PhonebookPDFController extends MultiActionController {
public ModelAndView handlePdf(HttpServletRequest req, HttpServletResponse resp) {
List entries = phonebookDao.getEntries();
return new ModelAndView("phonebook_pdfView", "phonebook", entries);
}
}
AbstractPdfViewpublic class PhonebookPdfView extends AbstractPdfView {
protected void buildPdfDocument(Map model, Document doc, PdfWriter writer,
HttpServletRequest req, HttpServletResponse resp) {
List phonebook = (List) model.get("phonebook");
// 使用 iText API 建立表格、字體、段落...
PdfPTable table = new PdfPTable(5);
table.addCell(new Phrase("Name", headerFont));
// ...
for (PhonebookEntry e : phonebook) {
table.addCell(new Phrase(e.getFirstName() + " " + e.getLastName(), bodyFont));
// ...
}
doc.add(table);
}
}
<bean id="pdfViewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="order" value="2"/>
<property name="basename" value="views-phonebook-pdf"/>
</bean>
# views-phonebook-pdf.properties
phonebook_pdfView.class=phonebook.pdf.PhonebookPdfView
*.pdf<servlet-mapping>
<servlet-name>phonebook</servlet-name>
<url-pattern>*.pdf</url-pattern>
</servlet-mapping>
Spring MVC 的視圖抽象層證明了「配置勝過程式碼」的威力,讓團隊能根據專案階段與團隊技能,靈活選擇最適合的視圖技術組合。