ASP.NET WebForm以「事件驅動、控件狀態自動保持、後置代碼分離邏輯」重新定義了Web開發體驗。Struts雖提供MVC但配置繁重、元素多、學習曲線陡峭。本文展示如何在JSP中輕量級借鑒WebForm思想,構建PageHandler模式實現「頁面顯示邏輯與控制邏輯分離」。
傳統JSP頁面混雜:HTML、Scriptlet業務邏輯、JavaScript、表單處理、多事件分發(如同一頁面含「保存」「刪除」「查詢」按鈕),導致單文件數千行、極難維護。
每個JSP頁面對應一個PageHandler類(類比WebForm代碼隱藏頁),封裝該頁面所有事件處理邏輯:
public abstract class PageHandler {
protected HttpServletRequest request;
protected HttpServletResponse response;
protected JspWriter out;
protected PageContext pageContext;
protected HttpSession session;
protected ServletContext application;
protected ServletConfig config;
protected String eventAction; // 頁面事件名(來自隱藏域 action)
protected String eventParams; // 事件參數(來自隱藏域 parameters)
public PageHandler(PageContext page) {
this.pageContext = page;
this.request = (HttpServletRequest) page.getRequest();
this.response = (HttpServletResponse) page.getResponse();
this.out = page.getOut();
this.application = page.getServletContext();
this.config = page.getServletConfig();
this.session = page.getSession();
try { request.setCharacterEncoding("GB2312"); } catch (Exception e) { e.printStackTrace(); }
}
/** 頁面初始化鉤子,子類覆寫 */
protected void onLoad() throws Exception {}
/** 事件分發:根據 action 反射調用同名方法 */
private void eventBind() throws Exception {
eventAction = request.getParameter("action");
if (eventAction != null && !eventAction.isEmpty()) {
eventParams = request.getParameter("parameters");
Class>[] paramTypes = { String.class };
Object[] paramValues = { eventParams };
Method method = getClass().getDeclaredMethod(eventAction, paramTypes);
method.setAccessible(true);
method.invoke(this, paramValues);
}
}
/** 統一入口:JSP中調用 handler.process() */
public final void process() throws Exception {
try {
onLoad();
eventBind();
} catch (Exception e) {
e.printStackTrace();
out.println("<script>alert('系統錯誤: " + e.getMessage() + "');</script>");
}
}
}
public class UserListHandler extends PageHandler {
public UserListHandler(PageContext page) { super(page); }
@Override
protected void onLoad() {
// 頁面首次加載:查詢用戶列表放入request
List<User> users = UserService.queryAll();
request.setAttribute("userList", users);
}
/** 對應頁面 action="deleteUser" */
public void deleteUser(String params) {
String userId = params; // 簡化:參數直接為ID
UserService.delete(userId);
onLoad(); // 重新加載列表
}
/** 對應 action="searchUser" */
public void searchUser(String params) {
String keyword = params;
List<User> users = UserService.search(keyword);
request.setAttribute("userList", users);
}
}
<%@ page import="com.example.handler.UserListHandler" %>
<%
UserListHandler handler = new UserListHandler(pageContext);
handler.process(); // 唯一邏輯入口
%>
<html><body>
<form method="post">
<input type="hidden" name="action" id="action"/>
<input type="hidden" name="parameters" id="parameters"/>
搜索:<input type="text" name="keyword" onchange="doAction('searchUser', this.value)"/>
<table>
<c:forEach var="u" items="${userList}">
<tr>
<td>${u.name}</td>
<td><button onclick="doAction('deleteUser', '${u.id}')>刪除</button></td>
</tr>
</c:forEach>
</table>
</form>
<script>
function doAction(action, params) {
document.getElementById('action').value = action;
document.getElementById('parameters').value = params;
document.forms[0].submit();
}
</script>
</body></html>
| 優勢 | 局限 |
|---|---|
| 邏輯剝離:JSP零Scriptlet,純模板渲染 | 反射調用有性能開銷(可緩存Method對象優化) |
| 事件驅動:一頁面多按鈕/操作清晰分發 | 狀態保持需手動管理(ViewState機制需自行實現) |
| 零配置:無XML、無框架依賴,純Java類 | 缺乏攔截器、國際化、驗證框架等企業級特性 |
| 平滑遷移:老JSP項目可漸進式重構 | 不適合大型團隊長期演進(建議遷移Spring MVC) |
@ActionMapping註解替代反射命名約定。Validator接口實現字段級驗證。ViewState對象(序列化到隱藏域/Session)模擬WebForm狀態保持。Controller -> Service -> View標準MVC,PageHandler作為Controller細化層。PageHandler模式以極低認知負擔實現「邏輯出頁面」,是JSP項目向現代化架構遷移的優秀踏腳石。