在 J2EE/Web 開發歷史上,Struts 1/2 曾長期霸佔 MVC 框架霸主地位;而 Spring MVC 憑藉 Spring 生態整合優勢、REST 原生支持、註解驅動開發體驗,成為現代 Spring Boot 時代的標準選擇。本文從架構原理、核心組件、開發體驗、性能擴展、生態遷移五個維度深度對比,為存量項目升級與新項目選型提供決策參考。
架構原理對比
| 維度 | Struts 2 | Spring MVC |
| 核心控制器 | FilterDispatcher (Servlet Filter) | DispatcherServlet (Servlet) |
| 請求處理流程 | 攔截器鏈 → Action 實例化/注入 → 調用方法 → Result 處理 | HandlerMapping → HandlerAdapter → Controller 方法 → ViewResolver |
| Action/Controller 實例策略 | 每請求一個實例 (原型模式),線程安全無需考慮實例變量 | 單例模式 (默認),必須無狀態,請求數據通過方法參數傳遞 |
| 數據綁定 | OGNL 表達式語言,功能強大但複雜、性能一般、歷史安全漏洞多 | 標準 JavaBean 綁定 + 轉換器 + @ModelAttribute,類型安全、可擴展 |
| 視圖解析 | Result 類型鏈 (JSP/FreeMarker/Velocity/JSON/Chain/Redirect) | ViewResolver 鏈 (JSP/Thymeleaf/FreeMarker/JSON/Excel/PDF 等) |
| 配置方式 | struts.xml + 註解 (Convention Plugin) | Java Config (@Configuration) + 註解 (@RequestMapping 等),無 XML |
核心代碼風格對比
Struts 2 Action
@Namespace("/user")
@Action(value = "profile", results = {
@Result(name = "success", location = "/WEB-INF/jsp/user/profile.jsp"),
@Result(name = "input", location = "/WEB-INF/jsp/user/edit.jsp")
})
public class UserProfileAction extends ActionSupport implements ModelDriven {
private UserService userService; // Spring 注入
private User user = new User(); // 實例變量:每請求一實例,線程安全
public String execute() {
user = userService.findById(getCurrentUserId());
return SUCCESS;
}
public String update() {
if (user.getEmail() == null || !user.getEmail().contains("@")) {
addFieldError("email", "郵箱格式錯誤");
return INPUT;
}
userService.update(user);
addActionMessage("更新成功");
return SUCCESS;
}
@Override public User getModel() { return user; }
// getter/setter for userService
}
Spring MVC Controller
關鍵差異深度解析
1. 線程安全模型:原型 vs 單例
- Struts 2:每個請求創建新 Action 實例 → 實例變量天然線程安全 → 開發者無需關注併發 → 但對象創建/GC 開銷大,高併發下壓力大
- Spring MVC:Controller 單例共享 → 嚴禁非 final 實例變量存儲請求狀態 → 所有數據通過方法參數 (局部變量) 傳遞 → 零鎖、高性能、但要求開發者有線程安全意識
2. 表單驗證與數據綁定
- Struts 2:
validate() 方法 + ActionSupport 錯誤收集 + OGNL 綁定巢狀對象 → 功能豐富但魔法多、調試難、OGNL 安全漏洞歷史包袱重
- Spring MVC:JSR-303
@Valid + BindingResult + 標準轉換器 → 規範化、類型安全、易測試、易擴展自定義 Validator
3. RESTful API 支持
- Struts 2:需配置
struts.mapper.action.prefix.enabled、ContentTypeInterceptor、JSON Plugin,體驗割裂
- Spring MVC:原生
@RestController、@RequestBody、ResponseEntity、@PathVariable、HttpMessageConverter 體系,REST 開發體驗極佳
4. 異步與響應式
- Struts 2:無原生異步支持,需手工集成 Servlet 3.0 AsyncContext
- Spring MVC:
DeferredResult、Callable、WebAsyncTask 原生異步;Spring WebFlux 提供完整響應式堆棧
5. 測試友好度
- Struts 2:依賴
StrutsTestCase 模擬容器,啟動慢、配置繁瑣
- Spring MVC:
MockMvc 單元測試極快、無容器、支持完整鏈路斷言
性能與擴展性基準 (參考值)
| 指標 | Struts 2 | Spring MVC | 說明 |
| 簡單請求吞吐 (QPS) | ~8,000 | ~15,000 | Spring MVC 單例無對象創建開銷 |
| 內存佔用 (併發 1000) | ~450 MB | ~280 MB | Action 實例大量創建/回收 |
| 啟動時間 | 較慢 (掃描 XML/註解) | 快 (Spring Boot 自動配置) | |
| 熱加載開發體驗 | 一般 | 優 (Spring Boot DevTools) | |
遷移路徑與共存策略
- 增量遷移:在
web.xml 中同時配置 StrutsPrepareAndExecuteFilter 與 DispatcherServlet,通過 URL 模式分流 (如 /api/* 走 Spring MVC,/legacy/* 走 Struts)
- 共享 Service 層:Spring 容器管理 Service,Struts 2 Action 通過
SpringBeanAutowiringInterceptor 或手工 WebApplicationContextUtils 獲取 Bean
- 逐模塊重寫:優先重寫新增模塊、高併發模塊、REST API 模塊
- 最終下線 Struts:移除 Filter、struts.xml、Action 類、OGNL 依賴
選型決策矩陣
| 場景 | 推薦框架 | 理由 |
| 新項目 (Spring Boot 生態) | Spring MVC / Spring WebFlux | 生態完整、開發效率高、長期維護 |
| 純 REST/微服務 API | Spring MVC (@RestController) | 原生 REST 支持、序列化靈活 |
| 傳統 Struts 2 存量項目維護 | Struts 2 (短期) → 規劃遷移 | 避免大規模重寫風險,制定遷移路線圖 |
| 複雜表單流程、向導式頁面 | Struts 2 (Workflow Interceptor) | 內置工作流攔截器適合多步驟表單 |
| 高併發、長連接、響應式 | Spring WebFlux | 非阻塞 IO、背壓、函數式編程 |
總結
Struts 2 以「每請求一實例」降低開發者線程安全門檻,曾是企業級 Web 開發標準;但其 OGNL 技術債、單例性能瓶頸、REST 支持薄弱、社區活躍度下降,已不適合現代雲原生應用開發。Spring MVC 憑藉單例無狀態設計、標準化數據綁定、原生 REST、完美 Spring 生態整合、響應式演進路徑,成為當前與可見未來 Java Web 開發的首選。存量 Struts 2 項目應制定「雙框架共存 → 增量遷移 → 完全替換」三階段現代化路線圖。