JSP 兩大開發模式
Model 1 : JSP 為中心 (頁面控制器)
請求 → JSP (接收參數、呼叫 JavaBean/Service、邏輯處理、生成回應) → 回應
- JSP 承擔 Controller + View 雙重角色
- 適合:小型專案、原型開發、簡單查詢/表單
- 缺點:Java 代碼與 HTML 混雜、難以維護、職責不清、難以單元測試
Model 2 : Servlet + JSP + JavaBean (MVC 架構)
請求 → Servlet (Controller: 參數接收、驗證、呼叫 Service、選擇 View、屬性設置)
↓
JavaBean/Service (Model: 業務邏輯、資料存取、資料封裝)
↓
JSP (View: 讀取屬性、生成 HTML 回應)
↓
回應
- 嚴格分離:Controller / Model / View
- 適合:中大型專案、團隊協作、長期維護、可測試性要求高
- 缺點:初期結構較複雜、類別數量多
Model 1 實戰:商品資訊錄入
1. Goods.java (Model/JavaBean)
package com.example.model;
public class Goods {
private String name;
private double price;
private String description;
// 無參建構子 + Getter/Setter...
}
2. GoodsDao.java (資料存取)
package com.example.dao;
public class GoodsDao {
public void saveGoods(Goods goods) {
// JDBC 插入邏輯...
}
}
3. index.jsp (輸入表單 View)
<form action="service.jsp" method="post">
商品名稱: <input name="name" /><br/>
價格: <input name="price" /><br/>
描述: <textarea name="description"></textarea><br/>
<input type="submit" value="提交" />
</form>
4. service.jsp (Controller + View 混合)
<% request.setCharacterEncoding("UTF-8"); %>
<jsp:useBean id="goods" class="com.example.model.Goods" />
<jsp:setProperty name="goods" property="*" /> <!-- 自動綁定表單 -->
<jsp:useBean id="goodsDao" class="com.example.dao.GoodsDao" />
<% goodsDao.saveGoods(goods); %> <%-- Java 代碼片段 (Scriptlet) --%>
<a href="index.jsp">返回繼續添加</a>
Model 2 實戰:相同功能 MVC 版
1. index.jsp (純 View - 輸入表單)
<form action="GoodsServlet" method="post">
商品名稱: <input name="name" /><br/>
價格: <input name="price" /><br/>
描述: <textarea name="description"></textarea><br/>
<input type="submit" value="提交" />
</form>
2. GoodsServlet.java (Controller)
package com.example.controller;
@WebServlet("/GoodsServlet")
public class GoodsServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
// 1. 參數接收
String name = req.getParameter("name");
String priceStr = req.getParameter("price");
String description = req.getParameter("description");
// 2. 封裝 Model
Goods goods = new Goods();
goods.setName(name);
goods.setPrice(Double.parseDouble(priceStr));
goods.setDescription(description);
// 3. 呼叫 Service/DAO
GoodsDao goodsDao = new GoodsDao();
goodsDao.saveGoods(goods);
// 4. 設置屬性供 View 使用 (或重定向)
req.setAttribute("message", "商品保存成功!");
req.getRequestDispatcher("/result.jsp").forward(req, resp);
}
}
3. result.jsp (純 View - 結果展示)
<h1>${message}</h1>
<a href="index.jsp">返回繼續添加</a>
4. web.xml (若不使用註解)
<servlet>
<servlet-name>GoodsServlet</servlet-name>
<servlet-class>com.example.controller.GoodsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>GoodsServlet</servlet-name>
<url-pattern>/GoodsServlet</url-pattern>
</servlet-mapping>
MVC 模式標準定義
| 層級 | 職責 | JSP Model 2 對應技術 | 核心原則 |
| Model (模型) | 資料狀態、業務規則、持久化 | JavaBean, DAO, Service, Entity | 不依賴 View/Controller,可獨立測試 |
| View (視圖) | 資料呈現、用戶互動介面 | JSP, Thymeleaf, FreeMarker, Velocity | 無業務邏輯,僅讀取 Model 屬性 |
| Controller (控制器) | 請求分發、參數處理、流程控制 | Servlet, Spring MVC @Controller | 薄控制器,不含業務邏輯,委派給 Service |
Model 1 vs Model 2 選型決策
| 因素 | 選 Model 1 | 選 Model 2 |
| 專案規模 | < 10 個頁面 | > 20 個頁面 |
| 團隊規模 | 1-2 人 | > 3 人 |
| 維護期限 | < 6 個月 | > 1 年 |
| 業務複雜度 | 簡單 CRUD | 複雜流程、事務、權限 |
| 測試需求 | 無自動化測試 | 需單元/整合測試 |
| 前後端分離 | 不分離 | 未來規劃分離 |
現代化演進:Spring MVC / Spring Boot
@Controller
@RequestMapping("/goods")
public class GoodsController {
@Autowired GoodsService goodsService;
@GetMapping("/add")
public String showForm() { return "goods/form"; } // 對應 form.html
@PostMapping("/add")
public String save(@Valid GoodsForm form, BindingResult result, Model model) {
if (result.hasErrors()) return "goods/form";
goodsService.save(form.toEntity());
model.addAttribute("message", "保存成功");
return "goods/result";
}
}
- 註解替代 web.xml
- 自動參數綁定 (
@ModelAttribute/@RequestParam)
- 驗證框架整合 (
@Valid + JSR-303)
- 視圖解析器自動映射邏輯視圖名到模板
- RESTful 風格 URL 設計