在當今的MVC framework裡似乎Webwork逐漸成為主流 Webwork+SpringFramework的組合變得越來越流行這似乎意味著Spring自帶的MVC framework遠比Webwork差所以大家紛紛用Webwork來代替確實Spring的MVC framework不算是整個Spring的核心部件但它的威力卻超過了很多人的想象很多人包括xiecc認為Spring的MVC framework是非常優秀的甚至比Webwork更優秀
下面列舉一下Spring的MVC framework在設計時做出的一些重要的決定並將之和相關的MVC framework如Webwork或struts進行對比
一Spring的整個MVC配置是基於IOC容器的
與struts或webwork相比這是一個ms有點奇怪的決定看一下Spring MVC的配置文件最先看到的不是action或者form而是一些有著特定名字的beanBean下面的配置是一些簡單或有點復雜的屬性我們看到的是機器更容易的數據結構而不是人更容易理解的元素
但是這恰恰是Spring的MVC強大的根源!因為它的配置就是Spring的核心IOC容器的配置這意味著所有IOC容器的威力都可以在這裡展現我們可以為所欲為地對Spring MVC進行擴展和增強我們可以完成在其它MVC framwork中很多難以想象的任務想擴展新的URL映射方式嗎?要換一個themeResolver或LocalReolver的實現嗎?想在頁面中顯示新類型的View(比如說RDF呵呵一個小秘密xiecc是研究語義網的雖然成天不務正業不寫論文只寫八卦)?甚至想直接在Controller裡定義AOP嗎?這些對Spring的MVC來說都是小菜一碟
我沒有仔細研究過Webwork的擴展機制我知道通過Webwork的interceptor機制可以進行很多的擴展甚至有一個簡單簡單的IOC容器但不管它有多強大提供了多少擴展點它的威力都很難和真正的IOC容器相比而struts的plugin功能則是出名的濫雖然它也提供了plugin機制
Spring采用IOC配置的另一個原因是使Spring的MVC與Spring的IOC容器的整合變得非常的容易Spring提供了與struts與webwork的整合但是這樣整合都需要在進行間接的包裝感覺總不是很自然而且還會導致一個概念多個配置webwork就需要在Spring裡配置bean再配置自己的xwork文件想象一下吧我們的bean直接就是一個controller直接可以完成MVC的所有任務這是多少爽的感覺
Rod Johnson采用IOC容器來實現的另一個原因是這會減少好多開發工作量看一下urlMapping吧它提供的property本身就是一個HashMap只有配置完成我們的bean裡的數據就自然存在了哈哈好爽吧不用象struts那樣解析XML再把它的內容一項一項地讀到HashMap裡
雖然這樣的配置會有點怪異但假如我們對Spring的IOC容器非常熟悉的話會發現它非常的親切也非常的簡單
最後是一個簡單的小秘密Spring怎麼知道某個bean的配置就是urlMapping?另一個bean的配置就是viewResolver?其實很簡單把所有的bean全部讀到內存裡然後通過bean的名字或類型去找就行了通過名字去找就是簡單的getBean方法通過類型去找則使用了BeanFactoryUtilsbeansOfTypeIncludingAncestors的靜態方法
二Spring提供了明確的Model View概念和相應的數據結構
在Spring裡有一個有趣的數據類型叫做ModelAndView它只是簡單地把要顯示的數據和顯示的結果封裝在一個類裡但是它卻提供了明確的MVC概念尤其是model概念的強化使程序的邏輯變得更清晰了
記得以前在Struts裡寫程序裡的時候為了顯示數據經常自己把東西放到HttpSession或HttpServletRequest裡(或set到form裡雖然不太有用)這造成了model概念的模糊而且也導致了struts與JSP頁面的緊耦合假如我們要替換成Veloctiy就得另外加一個plugin因為在velocity裡數據是不需要不放到request裡的
Webwork裡強調的是與Web framework解耦和它的command模式的簡單性因此在它的action裡只有簡單的get或set方法假如返回數據也只是簡單地返回一個String當然這樣的實現有它的好處但是它淡化了model和view的概念Rod Johnson認為Webwork裡的Action同時包含了Action和Model的職責這樣一個類的職責太多不是一個很好的設計當然Jason Carreira不太認同這種觀點因為Action裡的model對象完成可以delege給其它對象但不管怎樣這種爭論的根源在於Webwork裡淡化了model view甚至web的概念仁者見仁智者見智最後的結果還是看個人喜歡好吧
三Spring的Controller是Singleton的或者是線程不安全的
和Struts一樣Spring的Controller是Singleton的這意味著每個request過來系統都會用原有的instance去處理這樣導致了兩個結果我們不用每次創建Controller減少了對象創建和垃圾收集的時間由於只有一個Controller的instance當多個線程調用它的時候它裡面的instance變量不是線程安全的
這也是Webwork吹噓的地方它的每個Action都是線程安全的因為每過來一個request它就創建一個Action對象由於現代JDK垃圾收集功能的效率已經不成問題所以這種創建完一個對象就扔掉的模式也得到了好多人的認可Rod Johnson甚至以此為例證明JEE提供的object pool功能是沒多大價值的
但是當人們在吹噓線程安全怎麼怎麼重要的時候我想請問有多少人在多少情況下需要考慮線程安全?Rod Johnson在分析EJB的時候也提出過其它問題並不是沒有了EJB的線程安全魔法世界就會滅亡的大多數情況下我們根本不需要考慮線程安全的問題也不考慮object pool因為我們大多數情況下不需要保持instance狀態
至少我寫了那麼多的struts Action寫了那麼多的Spring Controller幾乎沒有碰到需要在instance變量保持狀態的問題當然也許是我寫的代碼不夠多Struts的設計者Craig R McClanahan曾經說當時他設計struts時有兩個條件不成熟當時沒有測試驅動開發的概念當時JVM的垃圾收集性能太次假如現在重新設計的話他也會采用每個request生成一個新對象的設計方法這樣可以解決掉線程安全的問題了
四Spring不象Webwork或tapestry那樣去隱藏Servlet相關的元素如HttpServletRequest或HttpServletResponse
這又是一個重要的設計決定在Webwork裡我們沒有HttpServletRequest或者HttpServletResponse只有getter setter或ActionContext裡數據這樣的結果導致一個干淨的Action一個與Web完全無關的Action一個可以在任何環境下獨立運行的bean那麼Webwork的這樣一個基於Command模式的Action究竟給我們帶來了什麼?我想主要有兩點
它使我們的Action可以非常容易地被測試
用戶可以在Action裡添加業務邏輯並被其它類重用
然而仔細跟Spring比較一下我們就會發現這兩點功能所帶來的好處其實並不象我們想象的那麼顯著Spring的Controller類也可以非常輕松被測試看一下springmock下面的包吧它提供的MockHttpServletRequest MockHttpServletResponse還有其它一些類讓測試Controller變得異常輕松再看一下Action裡的業務邏輯吧Jason Carreira曾經說我們可以盡情地在Webwork的Action裡加業務邏輯因為Action是不依賴於Web的但是有多少人真正往Action裡加業務邏輯的?大多數人都會業務邏輯delegate給另一個Service類或Manager類因為我們很清楚往Action裡加業務邏輯會使整個體系的分層架構變得不清晰不管怎樣Web層就是Web層業務層就是業務層兩者的邏輯混在一起總會帶來問題的而且往Action裡加業務邏輯會使用這個Action類變得龐大Webwork的Action是每個request都創建實例的盡管帶來的性能影響不太大但並不表示每次都要把業務邏輯再new出來業務邏輯在大多數的情況下應該是單例的
不把request和response展現給用戶當然還會帶來功能上的損失也許一般的場合用用webwork提供的接口已經足夠了但有時我們必須要知道request和response才能發揮出更大的威力比如我以前的一個項目裡有一個通過遞歸動態生成的樹狀結構的頁面在jsp頁面上顯示遞歸是痛苦或不可能的因此我用response直接write出頁面這在spring裡很easy但在webwork裡可能比較難了(偶不敢肯定偶研究得不夠深也許高手是有辦法的)
五Spring提供了不錯但不夠充分的interceptor機制
回頭看一下struts它在架構裡甚至沒有給我們提供hook point的機會我們沒有任何機會加入自己的interceptor我們只能通過重載struts的RequestProcessor類來進行一點有限的擴展
到了Webwork似乎interceptor一下子成了整個Framework的核心除了Action的核心部件其它所有的東西都是interceptor它的超強的interceptor功能使們擴展整個架構變得非常方便有人稱這種interceptor為AOPJason Carreira則自豪地宣稱這個叫做pragamtic AOP我不認同這是AOP它只是簡單的interceptor機制但不管如何它的interceptor確實有強大的功能
Spring也提供了它的interceptor機制它的HandlerInterceptor三個interceptor方法peHandle postHandle afterCompletion分別對應Controller執行前Controller執行後和page render之後雖然大多數情況下已經夠用但是從功能上來說顯然它沒有Webwork強大從AOP的角度來看它沒有提供around interceptor而只有before與after interceptor這意味著我們無法在interceptor前後保持狀態最簡單的情況假如我們要計算一個Controller的執行時間我們必須在執行完before後把begintime這個狀態保持住再在after裡把它調出來但是顯然這個狀態保持會是個問題我們不能把它放到instance變量裡因為interceptor不是線程安全的也許通過ThreadLocal可以解決這個問題但是如此簡單的功能要用到這樣的方法來處理顯然這個Interceptor本身設計上還是有點問題的
六Spring提供了MultiActionController使它可以在一個類裡包含多個Action
這個設計和struts的DispatchAction有點類似只不過提供了更靈活的機制當我們的項目變大的時候把功能類似的方法放到同一個Action裡完全值得的!Webwork缺少這樣的機制假如看一下Spring的源代碼會發現其實實現MultiActionController的工作量相當的少只不過是用反射機制把解析出來的方法名執行一下就完事了其實Webwork也完全可以提供這樣的機制雖然從設計上來說確實不是很優雅但是它確實很有用
七Spring提供了更多的選擇方式
看看Spring裡提供的Controller吧它提供了好多不同的Controller類要生成Wizard嗎?要專門用於提交form的Controller嗎?要執多個方法的類嗎?Spring提供了豐富的子類來擴展這些選擇當然我們還可以很輕松地自己擴展這些功能
再看看Spring的ViewResolver吧 它提供了無數不同類型的ViewResolver更重要的是我們自定義我們的頁面映射方式看看strtus看看webwork都會存在頁面與forward name的一層間接轉換我們必須在配置文件裡配置好某個字符串(典型的是success)對應的是那個頁面但是Spring裡我們有了更大的自由度我們可以采用webwork的策略也可以采用更簡單的策略如將JSP文件名去掉擴展名的映射方法也許有人認為這種映射方式很幼稚但是我覺得它是非常有用的方式即使在大項目裡
還有新的擴展嗎?看看Spring Web Flow吧它是SpringFramework的子項目它為一長串的基於頁面流的Wizard頁面提供了可配置的實現方式在Spring 裡它將是SpringFramework的一部分
八Spring的tag
盡管Spring的tag數量上少得可憐但它卻是精心設計的它的目標很簡單讓美工可以輕松地編輯頁面因為在Spring的頁面裡Text仍然是Textcheckbox仍然是CheckBox而不象在struts或webwork中的Tag它只是用Springbind對輸入內容進行了一下包裝所以盡管頁面顯示代碼上會比Webwork多但這絕對是有價值的
在接下來的幾章裡我會分析一下Spring是如何讓我們的Web應用不需要知道ApplicationContext就能夠訪問IOC容器的然後會對Spring的設計和執行過程進行簡單的源碼分析然後給出幾個擴展Spring MVC的方法
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28621.html