一、Spring 表單標簽庫
自 Spring 2.0 起提供 spring-form.tld,生成 HTML 4.01/XHTML 1.0 兼容標簽,實現雙向數據綁定。引入方式:
<%@ taglib prefix="form" uri="https://www.springframework.org/tags/form" %>
核心屬性
- path:綁定屬性路徑(最關鍵),對應
modelAttribute.getXxx()。
- cssClass / cssStyle:樣式類 / 行內樣式。
- htmlEscape:是否 HTML 轉義,默認 true。
- cssErrorClass:驗證錯誤時覆蓋 cssClass。
1. form + input 綁定對象
<!-- Controller -->
model.addAttribute("product", new Product("Meizu note1", 999));
return "bar/action11";
<!-- JSP -->
<form:form modelAttribute="product">
<form:input path="name" cssClass="textCss" htmlEscape="false"/>
<form:input path="price"/>
</form:form>
渲染後:
<form id="product" action="..." method="post">
<input id="name" name="name" class="textCss" type="text" value="Meizu note1<hr/>"/>
<input id="price" name="price" type="text" value="999.0"/>
</form>
htmlEscape="false" 允許原樣輸出 HTML 標簽(慎用 XSS 風險)。
2. checkbox 三種綁定模式
| 綁定類型 | 選中條件 | 示例 |
| Boolean | 值為 true | path="isMarried" |
| 數組/集合 | 值在集合中 | path="hobbies" value="讀書" 多個同 path 不同 value |
| 其他類型 | 值等於綁定值 | path="education" value="本科" |
渲染會自動生成隱藏域 _propertyName 確保未勾選也能提交值。
3. radiobutton 單選組
<form:radiobutton path="education" value="專科"/>專科
<form:radiobutton path="education" value="本科"/>本科
<form:radiobutton path="education" value="研究生"/>研究生
同 path 不同 value,自動按綁定值選中。
4. password 密碼框
<form:password path="pwd" showPassword="true"/> <!-- 顯示明文 -->
<form:password path="pwd" showPassword="false"/> <!-- 默認掩碼 -->
5. select / option / options 下拉框
<!-- 單選 -->
<form:select path="education" items="${productTypes}" itemLabel="name" itemValue="id"/>
<!-- 多選 -->
<form:select path="education" multiple="true" size="3" items="${productTypes}" itemLabel="name" itemValue="id"/>
<!-- 手動 option + options 混用實現「請選擇」 -->
<form:select path="education">
<form:option value="">--請選擇--</form:option>
<form:options items="${productTypes}" itemLabel="name" itemValue="id"/>
</form:select>
6. textarea、errors、hidden
<form:textarea path="desc" rows="3" cols="20"/>
<form:errors path="desc"/> <!-- 顯示驗證錯誤,生成 <span> -->
<form:hidden path="id"/> <!-- 隱藏域,保持狀態 -->
7. radiobuttons / checkboxes 批量生成
<form:radiobuttons path="education" items="${productTypes}" itemLabel="name" itemValue="id" delimiter="," element="a"/>
<form:checkboxes path="hobbies" items="${productTypes}" itemLabel="name" itemValue="id" delimiter="-"/>
delimiter 分隔符,element 包裹標簽(默認 span)。
二、視圖解析器
核心接口
ViewResolver:邏輯視圖名 → 真實 View 對象映射。
View:渲染模型數據到響應。
常用解析器體系
| 解析器 | 特點 | 適用場景 |
| InternalResourceViewResolver | JSP/JSTL,前綴+後綴拼接 URL,最常用 | 傳統 JSP 視圖 |
| FreeMarkerViewResolver | FreeMarker 模板,繼承 UrlBasedViewResolver | 現代模板引擎 |
| VelocityViewResolver | Velocity 模板 | 老項目維護 |
| XmlViewResolver | XML 文件定義邏輯名→View Bean 映射 | 靜態視圖映射 |
| ContentNegotiatingViewResolver | 根據 Accept Header 選擇視圖(JSON/HTML/PDF) | RESTful 多格式輸出 |
鏈式視圖解析器
配置多個解析器,通過 order 控制優先級(值越小越優先)。InternalResourceViewResolver 必須放最後(總能解析成功,返回非空 View)。
<!-- FreeMarker 優先 order=1 -->
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="order" value="1"/>
<property name="suffix" value=".html"/>
<property name="prefix" value=""/>
<property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"/>
<property name="exposeSpringMacroHelpers" value="true"/>
<property name="requestContextAttribute" value="rc"/>
</bean>
<!-- JSP 兜底 order=2 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="2"/>
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
</bean>
三、FreeMarker 多視圖整合實戰
1. 依賴
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.0.RELEASE</version>
</dependency>
2. 配置 FreeMarkerConfigurer
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/htmlviews"/>
<property name="freemarkerSettings">
<props>
<prop key="template_update_delay">5</prop>
<prop key="defaultEncoding">UTF-8</prop>
<prop key="classic_compatible">true</prop>
<prop key="locale">zh_CN</prop>
<prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
<prop key="number_format">0.######</prop>
</props>
</property>
</bean>
3. Controller 雙視圖演示
@Controller @RequestMapping("/foo")
public class FooController {
@RequestMapping("/jstl")
public String jstl(Model model) {
model.addAttribute("message", "Hello JSTL View!");
return "foo/jstl"; // 解析為 /WEB-INF/views/foo/jstl.jsp
}
@RequestMapping("/ftl")
public String ftl(Model model) {
model.addAttribute("users", new String[]{"tom","mark","jack"});
model.addAttribute("message", "Hello FreeMarker View!");
return "foo/ftl"; // 解析為 /WEB-INF/htmlviews/foo/ftl.html
}
}
4. 視圖文件
jstl.jsp:${message}
ftl.html:
<p>${message}</p>
<ul>
<#list users as user><li>${user}</li></#list>
</ul>
5. 運行效果
/foo/ftl → FreeMarkerViewResolver (order=1) 找到 ftl.html → 渲染成功。
/foo/jstl → FreeMarkerViewResolver 找不到 → InternalResourceViewResolver (order=2) 找到 jstl.jsp → 渲染成功。
四、綜合 CRUD 示例:產品管理
技術棧
Spring MVC 4.3 + Maven + MySQL + JSTL + jQuery
核心代碼結構
- Entity:Product(id、name、price、ProductType)、ProductType
- Service:ProductService(CRUD、搜索、批量刪除)、ProductTypeService
- Controller:ProductController(index、add、addSave、edit、editSave、delete、deletes)
- View:index.jsp(列表+搜索+批刪)、add.jsp、edit.jsp、main.css
關鍵 Controller 片段
@Controller @RequestMapping
public class ProductController {
@Autowired ProductService productService;
@Autowired ProductTypeService productTypeService;
@RequestMapping
public String index(Model model, String searchKey) {
model.addAttribute("products", productService.getProductsByName(searchKey));
model.addAttribute("searchKey", searchKey);
return "product/index";
}
@RequestMapping("/add")
public String add(Model model) {
model.addAttribute("product", new Product());
model.addAttribute("productTypes", productTypeService.getAllProductTypes());
return "product/add";
}
@RequestMapping("/addSave")
public String addSave(Model model, Product product) {
try {
product.setProductType(productTypeService.getProductTypeById(product.getProductType().getId()));
productService.addProduct(product);
return "redirect:/";
} catch (Exception e) {
model.addAttribute("product", product);
model.addAttribute("productTypes", productTypeService.getAllProductTypes());
model.addAttribute("message", e.getMessage());
return "product/add";
}
}
// edit/editSave/delete/deletes 類似...
}
列表頁核心 JSTL
<form method="get">
名稱:<input name="searchKey" value="${searchKey}"/>
<input type="submit" value="搜索"/>
</form>
<form action="deletes" method="post">
<table class="tab">
<c:forEach var="p" items="${products}">
<tr>
<td><input type="checkbox" name="id" value="${p.id}"/></td>
<td>${p.id}</td><td>${p.name}</td><td>${p.price}</td><td>${p.productType.name}</td>
<td>
<a href="delete/${p.id}" class="abtn">刪除</a>
<a href="edit/${p.id}" class="abtn">編輯</a>
</td>
</tr>
</c:forEach>
</table>
<input type="submit" value="刪除選擇項"/>
<a href="add" class="abtn">添加</a>
</form>
表單頁綁定
<form:form action="addSave" modelAttribute="product">
<form:input path="name"/>
<form:select path="productType.id" items="${productTypes}" itemLabel="name" itemValue="id"/>
<form:input path="price"/>
<form:hidden path="id"/> <!-- 編輯時保持 ID -->
<input type="submit" value="保存"/>
</form:form>
五、總結與擴展
- 表單標簽:掌握 path 綁定、三種 checkbox 模式、select/options 混用、errors 顯示驗證錯誤。
- 視圖解析器鏈:order 控制優先級,InternalResourceViewResolver 兜底,支援 JSP/FreeMarker/Velocity 共存。
- CRUD 最佳實踐:PRG 模式、@ModelAttribute 綁定、服務層異常轉換、批量操作數組參數、重定向傳遞提示信息。
- 進階方向:RESTful API 設計、@Valid + BindingResult 驗證、異步處理、文件上傳下載、國際化、攔截器權限控制。
- 上一篇文章: JSP 基本語法大全:三種 Java 代碼塊、三大指令、九大內置對象
- 下一篇文章: Java 基礎核心:運算符優先級、堆棧記憶體、數組與關鍵字詳解
Copyright ©
程式碼之家 All Rights Reserved