【Servlet規范】
在Servlet規范中所定義的請求處理響應接口是這樣的:

我們可以看到,Servlet的基本接口定義中:
參數列表 —— Http請求被封裝為一個HttpServletRequest對象(或者ServletRequest對象),而Http響應封裝為一個HttpServletResponse對象(或者ServletResponse對象)
返回值 —— 方法不存在返回值(返回值為void)
在這個設計中,HttpServletRequest和HttpServletResponse承擔了完整的處理Http請求的任務。而這兩個Servlet對象的職責也有所分工:
HttpServletRequest對象 —— 主要用於處理整個Http生命周期中的數據。
HttpServletResponse對象 —— 主要用於處理Http的響應結果。
這里實際上有一點“數據與行為分離”的意味。也就是說,在Servlet處理請求的過程中,其實也是Servlet中響應方法內部的邏輯執行過程中,如果需要處理請求數據或者返回數據,那么我們需要和HttpServletRequest打交道;如果需要處理執行完畢之后的響應結果,那么我們需要和HttpServletResponse打交道。 這樣的設計方式,是一種 骨架式 的設計方式。因為Servlet是我們進行Web開發中最底層的標准,所以我們可以看到接口設計中的返回值對於一個最底層標准而言毫無意義。因為不存在一個更底層的處理程序會對返回值進行進一步的處理,我們不得不在Servlet的過程中自行處理瀏覽器的行為控制。
Struts1.X是一個較為早期的MVC框架實現,它的歷史最早可以追溯到2000年,作為Apache開源組織的一個重要項目,取名為“Struts”,有“基礎構建”的含義。在那個程序框架尚處於朦朧階段的年代,“基礎構建”無疑是每個程序員夢寐以求的東西。
對於Struts1.X,我們還是把關注的重點放在Struts中的Controller層的定義上:
- public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response);
如果和之前的Servlet模型加以比較我們就可以發現,Struts1.X對於基本的Servlet模型做了一定的擴展和重構:
- 保留了HttpServletRequest和HttpServletResponse這兩大接口作為參數
- 將返回值改為ActionForward,並由Struts1.X框架通過處理ActionForward完成對響應結果的處理
- 增加了ActionMapping和ActionForm兩大參數,前者表示Http請求的一個簡要概括,后者表示一個數據模型,用以承載整個請求生命周期中的數據
由於Struts1.X已經不再是一個底層的實現規范,於是響應方法“返回值”被框架引入,加入到了整個處理過程之中。我們可以看到,在這里最大的進步之處就在於: 引入了新的編程元素,從而優化整個邏輯處理過程。 編程元素的引入非常重要,因為對於一個任何一個程序員而言,充分調用所有可以利用的編程要素是衡量一個程序寫得好壞的重要標准。之后,我們還可以看到其他的框架在引入編程元素這個方面所做的努力。
【Webwork2 / Struts2】
Apache社區與Opensymphony開源組織在2005年底宣布未來的Struts項目將與Webwork2項目合並,並聯合推出Struts2,通過Apache社區的人氣優勢與OpenSymphony的技術優勢,共同打造下一代的Web層開發框架。這也就是Struts2的由來。 從整個過程中,我們可以發現, Webwork2和Struts2是一脈相承的Web層解決方案。 優勢突出表現為對Controller的徹底改造:
- public class UserController {
- private User user
- public String execute() {
- // 這里加入業務邏輯代碼
- return "success";
- }
- // 這里省略了setter和getter方法
- }
- <action name="register" class="com.demo2do.sandbox.web.UserController" method="register">
- <result name="success">/register-success.jsp</result>
- </action>
- 在Controller中徹底杜絕引入HttpServletRequest或者HttpServletResponse這樣的原生Servlet對象。
- 將請求參數和響應數據都從響應方法中剝離到了Controller中的屬性變量。
當然,這種改造的前提條件在於Webwork2 / Struts2引入了另外一個重要的編程概念:ThreadLocal模式。使得Controller成為一個線程安全的對象被Servlet模型所調用,這也就突破了傳統Servlet體系下,Servlet對象並非一個線程安全的對象的限制條件。
從引入新的編程元素的角度來說,Webwork2 / Struts2無疑也是成功的。因為在傳統Servlet模式中的禁地Controller中的屬性變量被合理利用了起來作為請求處理過程中的數據部分。這樣的改造不僅使得表達式引擎能夠得到最大限度的發揮,同時使得整個Controller看起來更像是一個POJO。因而,這種表現形態被筆者冠以的名稱是: POJO實現模式 。
POJO實現模式是一種具有革命性意義的模式,因為它能夠把解耦合這樣一個觀點發揮到極致。從面向對象的角度來看,POJO模式無疑也是所有程序員所追求的一個目標。這也就是Webwork2 / Struts2那么多年來經久不衰的一個重要原因。
【SpringMVC】
相比較Webwork2 / Struts2,SpringMVC走了一條比較溫和的改良路線。因為SpringMVC自始至終都沒有突破傳統Servlet編程模型的限制,而是在這過程中不斷改良,不斷重構,反而在發展中開拓了一條嶄新的道路。
我們可以看看目前最新版本的SpringMVC中對於Controller的定義:
- @Controller
- @RequestMapping
- public class UserController {
- @RequestMapping("/register")
- public ModelAndView register(String email, String password) {
- // 在這里調用具體的業務邏輯代碼
- return new ModelAndView("register-success");
- }
- }
1. 使用參數-返回值(Param-Return)實現模式來打造Controller
方法的參數(email和password)被視作是Http請求參數的概括。而在這里,它們已經被SpringMVC的框架有效處理並屏蔽了內在的處理細節,呈現出來的是與請求參數名稱一一對應的參數列表。而返回值ModelAndView則表示Http的響應是一個數據與視圖的結合體,表示Http的處理結果。
2. 引入Annotation來完成請求-響應的映射關系
引入Annotation來完成請求-響應的映射關系,是SpringMVC的一個重大改造。在早期的SpringMVC以及其他的MVC框架中,通常都是使用XML作為基礎配置的。而Annotation的引入將原本分散的關注點合並到了一起,為實現配置簡化打下了堅實的基礎。
3. 泛化參數和返回值的含義
這是一個蘊含的特點。事實上,SpringMVC在響應方法上,可以支持多種多樣不同的參數類型和返回值類型。例如,當參數類型為Model時,SpringMVC將會自動將請求參數封裝於Model內部而傳入請求方法;當返回值類型是String時,直接表示SpringMVC需要返回的視圖類型和視圖內容。當然,這些泛化的參數和返回值的內容全部都由SpringMVC在框架內部處理了。
SpringMVC雖然是一個溫和的改良派,卻是在改良這個領域做得最為出色的。以引入Annotation為例,引入Annotation來完成請求-響應映射,不正是我們反復強調的引入並合理使用新的編程元素來完成處理任務嘛?而泛化后的參數和返回值,則可以讓程序員在寫Controller的代碼時可以隨心所欲,不再受到任何契約的束縛,這樣一來接口的邏輯語義也就能夠更加清晰。
MVC模型的發展軌跡

目前,Struts1.X這一條路被證明已經窮途末路;另外的兩條發展軌跡總體來說實力相當,SpringMVC大有趕超之勢。
那么,為什么曾經一度占領了大部分市場的Struts2會在近一段時間內被SpringMVC大幅趕超呢?這里面的原因多種多樣,有自身架構上的原因,有設計理念上的原因,但是筆者認為, 其本質原因還是在於Struts2對於技術革新的力度遠不及SpringMVC,后者試用更簡單,開發、運行速度更快。
如果我們回顧一下Struts2過去能夠獨占鰲頭的原因就可以發現,Struts2的領先在於編程模型上的領先。其引入的POJO模型幾乎是一個殺手級的武器。而基於這一模型上的攔截器、OGNL等技術的支持使得其他的編程模型在短時間很難超越它。 但是隨着時代的發展,Struts2在技術革新上的作為似乎步子就邁得比較小。我們可以看到,在JDK1.5普及之后,Annotation作為一種新興的Java語法,逐漸被大家熟知和應用。這一點上SpringMVC緊跟了時代的潮流,直接用於請求-響應的映射。而Struts2卻遲遲無法在單一配置源的問題上形成突破。