許多團隊因不知 Struts 具備強大擴展機制,而自行開發 MVC 框架。本文深度解析 Struts 三大核心擴展點——PlugIn (啟動/關閉)、RequestProcessor (請求處理流程)、ActionServlet (核心控制器)——並以 Hibernate 整合、權限攔截、動態 Content-Type 為實戰案例,展示如何在框架內優雅滿足業務需求。
| 擴展點 | 觸發時機 | 適用場景 | 優先順序 |
|---|---|---|---|
| PlugIn | 應用啟動/關閉 | 框架初始化 (Hibernate、Log4j、資源載入)、清理資源 | 最高 (首選) |
| RequestProcessor | 每次請求處理流程中 | 權限檢查、編碼設定、動態 Content-Type、自定義 Forward 邏輯 | 次之 |
| ActionServlet | 啟動/關閉 + 每次請求 | 前兩者無法滿足的極端需求 (如自定義模組載入邏輯) | 最低 (最後手段) |
public interface PlugIn {
void init(ActionServlet actionServlet, ModuleConfig config) throws ServletException;
void destroy();
}
public class HibernatePlugIn implements PlugIn {
private String configFile; // 透過 Setter 注入
public void init(ActionServlet servlet, ModuleConfig config) throws ServletException {
System.out.println("Initializing Hibernate with config: " + configFile);
// 實際邏輯:建立 SessionFactory、註冊至 JNDI/ApplicationContext
// Configuration cfg = new Configuration().configure(configFile);
// SessionFactory sf = cfg.buildSessionFactory();
// servlet.getServletContext().setAttribute("HibernateSessionFactory", sf);
}
public void destroy() {
System.out.println("Shutting down Hibernate SessionFactory");
// 關閉 SessionFactory、釋放連線池
}
// Getter/Setter 供 struts-config.xml 注入
public String getConfigFile() { return configFile; }
public void setConfigFile(String configFile) { this.configFile = configFile; }
}
<struts-config>
<!-- 其他配置 -->
<plug-in className="com.app.util.HibernatePlugIn">
<set-property property="configFile" value="/hibernate.cfg.xml" />
</plug-in>
</struts-config>
public void process(HttpServletRequest req, HttpServletResponse res) {
request = processMultipart(request); // 1. 檔案上傳包裝
path = processPath(request, response); // 2. 解析路徑
processLocale(request, response); // 3. Locale 解析
processContent(request, response); // 4. Content-Type 設定
processNoCache(request, response); // 5. 快取控制
if (!processPreprocess(request, response)) return; // 6. 前置處理 Hook
mapping = processMapping(request, response, path); // 7. 尋找 ActionMapping
if (!processRoles(request, response, mapping)) return; // 8. 角色授權
form = processActionForm(request, response, mapping); // 9. FormBean 管理
processPopulate(request, response, form, mapping); // 10. 表單資料填充
if (!processValidate(request, response, form, mapping)) return; // 11. 驗證
if (!processForward(request, response, mapping)) return; // 12. Forward 導向
if (!processInclude(request, response, mapping)) return; // 13. Include 包含
action = processActionCreate(request, response, mapping); // 14. Action 實例化
forward = processActionPerform(request, response, action, form, mapping); // 15. 執行業務
processForwardConfig(request, response, forward); // 16. 處理返回 Forward
}
需求:
public class CustomRequestProcessor extends RequestProcessor {
// 覆寫前置處理 Hook:登入攔截
@Override
protected boolean processPreprocess(HttpServletRequest req, HttpServletResponse res) {
// 登入頁面自身放行
String path = req.getServletPath();
if ("/loginInput.do".equals(path) || "/login.do".equals(path)) {
return true;
}
HttpSession session = req.getSession(false);
if (session != null && session.getAttribute("userName") != null) {
return true; // 已登入
}
// 未登入:導向登入頁並短路流程
try {
req.getRequestDispatcher("/Login.jsp").forward(req, res);
} catch (Exception e) {
// log error
}
return false; // 關鍵:返回 false 終止後續處理
}
// 覆寫 Content-Type 設定:動態圖片回應
@Override
protected void processContent(HttpServletRequest req, HttpServletResponse res) {
if ("/contactimage.do".equals(req.getServletPath())) {
res.setContentType("image/gif");
return;
}
super.processContent(req, res); // 其他路徑走預設邏輯
}
}
<controller>
<set-property property="processorClass" value="com.app.util.CustomRequestProcessor" />
</controller>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param><param-name>config</param-name><param-value>/WEB-INF/struts-config.xml</param-value></init-param>
<load-on-startup>1</load-on-startup>
</servlet>
public class CustomActionServlet extends ActionServlet {
@Override
public void init() throws ServletException {
super.init(); // 必須呼叫父類初始化
// 自定義啟動邏輯:載入自定義模組、註冊 MBean...
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// 自定義請求預處理...
super.doPost(req, res); // 委派父類處理
}
@Override
public void destroy() {
// 自定義關閉邏輯...
super.destroy();
}
}
<servlet-class>com.app.CustomActionServlet</servlet-class>
業務需求
│
├─ 應用啟動/關閉初始化資源? → 是 → PlugIn
│
├─ 請求處理流程中特定邏輯? → 是 → RequestProcessor (覆寫具體 processXxx 方法)
│ │
│ ├─ 權限/登入攔截 → processPreprocess
│ ├─ 編碼/Content-Type → processContent/processNoCache
│ ├─ ActionMapping 查找邏輯 → processMapping
│ ├─ FormBean 管理 → processActionForm/processPopulate/processValidate
│ └─ Forward/Include 邏輯 → processForward/processInclude
│
└─ 以上皆無法滿足? → ActionServlet (極少見)
Struts 擴展機制遵循「配置優於繼承、專用優於通用」原則:
掌握三大擴展點,配合 `struts-config.xml` 宣告式配置,即可在不修改框架核心代碼前提下,滿足 95% 以上的企業級定制需求。這正是 Struts 作為成熟框架的架構韌性所在。