人人都愛Spring加Hibernate。
????但Spring MVC+hibernate的Sample如Appfuse的代碼卻算不得最簡潔優美好讀,如果在自己的項目中繼續發揮我們最擅長的依樣畫葫蘆大法,美好愿望未必會實現。
???? 所以,Pramatic精神不滅。這個系列就是探尋最適合自己的Spring+Hibernate模式。
????
?????????????????????????????
I-配置文件簡化?????我厭倦一切配置文件繁重的框架。
???? 最好的情況是,框架提供極端靈活復雜的配置方式,但只在你需要的時候。
?
???? Spring提供了三種可能來簡化XML。隨著國內用戶水平的提高,這些基本的簡化技巧大家都已掌握。
?????大家可以直接看第3,第4點--Spring 1.2, Spring 2.0的后繼改進。
1.1.autowire="byName" /"byType"
???? 假設Controller有一個屬性名為customerDAO,Spring就會在配置文件里查找有沒有名字為CustomerDAO的bean, 自動為Controller注入。
?????如果bean有兩個屬性,一個想默認注入,一個想自定義,只要設定了autowire,然后顯式的聲明那個想自定義的,就可以達到要求。這就應了需求,在需要特別配置的時候就提供配置,否則給我一個默認注入。
???? 還有一個更懶的地方,在最最根部的<beans>節點寫一句default-autovwrie="byName",可以讓文件里的所有bean 都默認autowrie。
??? 不過Rod認為開發期可以這樣,但Production Server上不應該使用Autowire。而我覺得那些自定義一次的地方比如TranscationManager應該詳細定義,而Dao,Controller這種大量重復定義的bean就可以偷點懶了。
1.2.<bean>節點之間抽象公共定義和 Inner Bean
??? 這太方便懶人了,想不到兩個獨立的XML節點都可以玩繼承和派生,子節點擁有父節點的全部屬性。
??? 最好用的地方就是那個Transtion Proxy的定義。先定義一個又長又冗的父類,然后用子類去繼承它。
???
??? 另外,還有一個Inner Bean的機制,可以把DAO寫成Proxy的內部類。為什么要寫成內部類?為了讓Proxy冒名頂替它去讓Controller Autowire。(詳見后面的示例)
1.3. 寬松的配置, To XML or Not to XML?
??? 據說Spring比Struts的配置寬松了很多,這就給人把東西從配置文件中撤回原碼中的機會。
??? 不贊成什么都往配置文件里曬,造成了Rich Information的配置文件,修改或者查看的時候,要同時打開配置文件和原碼才能清楚一切。
??? 而我希望配置文件就集中做一些整體的配置,還有框架必須的、無需管理的冗余代碼。而一些細節的變化不大的配置和邏輯,就盡量別往里塞了。因此,Success/Fail View 的配置,不建議放在里面。
2.簡化后的配置文件
1.Controller只剩下一句
<bean?name="customerController"?class="org.springside.bookstore.web.CustomerController"?autowire="byName"/>
2.DAO也只剩一句
<bean?id="customerDAO"?class="org.springside.bookstore.dao.CustomerDao"/>
3.Service類只剩下5行
??<bean?id="customerManager"?parent="baseTxService">
????????<property?name="target">
????????????<bean?class="org.springside.bookstore.service.CustomerManager"/>
????????</property>
????</bean>
3.Spring 1.2后xml語法簡化
?最主要的簡化是把屬性值和引用bean從子節點變回了屬性值,對不喜歡autowire的兄弟比較有用。
?當然,如果value要CDATA的時候還是要用子節點。另外,list的值可以用空格隔開也比較實用。
?
<property?name="myFriendList">
??<list>
?????<value>gigix</value>
??????? <value>wuyu</value>
??</list>
</property>
簡化為
<property?name="myFriendList"?value="gigix wuyu"/>

???
??
4.Spring 2.0來了?? 如果沒什么外力刺激,spring xml 可能就這樣不會變了。但現在xml成了過街老鼠,被ror的默認配置和JDK5的annotation逼得不行,當然就要繼續求變。
?? 比如有好事者認為,節點名必須以bean打頭,附加一個屬性id來表示bean名;屬性值必須搞一個property子節點,子節點上有個屬性name來表示屬性名,是給機器看的很不直觀的東西。
<bean?id="customerDAO" class="org.springside...CustomerDAO">
?<property?name="maxCount"?value="10">
</bean>
給人看的東西應該就寫成
<customerDAO?class="org.springside....CustomerDAO"?maxCount="10"/>
Spring 2.0正用schema實現類似的語法,具體請看它的JPetStore sample。
5.使用Spring自帶的DTD使編輯器Smart.
??? 如果沒有用Eclipse的Spring插件,那至少也要使用spring自帶的dtd使XML編輯器smart一些,能夠自動為你生成屬性,判斷節點/屬性名稱有沒有拼錯等。
6.還有更變態的簡化配置方法
?? ?比如autoproxy,不過我覺得更簡化就不可控了,所以沒有采用。
因為Spring自帶的sample離我們的實際項目很遠,所以官方一點的model層模式展現就靠Appfuse了。
????但Appfuse的model層總共有一個DAO接口、一個DAOImpl類、一個Service接口、一個ServiceImpl類、一個DataObject.....大概只有受慣了虐待的人才會欣然接受吧。
??? 另外,Domain-Driven逢初一、十五也會被拿出來討論一遍。
??? 其實無論什么模式,都不過是一種人為的劃分、抽象和封裝。只要在團隊里理解一致,自我感覺優雅就行了。
???? 我的建議是,一開始DO和Manager一生一旦包演全場,DO作為純數據載體,而Manager類放置商業方法,用getHibernateTemplate()直接訪問數據庫,不強制基于接口編程。當某天系統復雜到你直覺上需要將DAO層和Service層分開時,再分開就好了。
??? 1.DataObject類
???? 好聽點也可以叫Domain Object。Domain Driven? Development雖然誘人,但因為Java下的ORM框架都是基于Data Mapper模式的,沒有Ruby On Rails中那種Active Recorder的模式。所以,還是壓下了這個欲望,Data Object純粹作一個數據載體,而把數據庫訪問與商業邏輯操作統一放到Manager類中。
??? 2.Manager類
????我的Manager類是Appfuse中DAO類與Service類的結合體,因為:
????2.1 不想使用純DAO
???? 以往的DAO是為了透明不同數據庫間的差異,而現在Hibernate已經做的很好。所以目前純DAO的更大作用是為了將來可以切換到別的ORM方案比如iBatis,但一個Pragmaic的程序員顯然不會無聊到為了這個機會不大的理由,現在就去做一個純DAO層,項目又不是Appfuse那樣為了demo各種ORM方案而存在。
??? 2.2 也不使用純的薄Service層
??? 在JPetStore里有一個很薄的Service層,Fascade了一堆DAO類,把這些DAO類的所有方法都僵硬的重復了一遍。而我認為Fascade的意義在二:
????一是Controller調用Manager甲的時候,總會伴隨著調用Manager乙的某些方法。使用Fascade可以避免Controller零散的調用一堆Manager類。
????二是一個商業過程里可能需要同時調用DAO甲乙丙丁的方法。?
?????這些時候,Fascade都是合理的。但我討厭類膨脹,所以我寧愿在甲乙丙丁中挑一個來充當Fascade的角色。有耦合的問題嗎?對一個不是死搬書的Designer來說,組件邊界之內的類之間的耦合并不是耦合。
??? 3.去除不必要的基于接口編程
??? 眾所周知,Spring是提倡基于接口編程的。
??? 但有些Manager類,比如SaleOrderManager ,只有5%的機會再有另一個Impl實現。95%時間里這兩兄弟站一起,就像C++里的.h和.cpp,徒增維護的繁瑣(經常要同步兩個文件的函數聲明),和代碼瀏覽跳轉時的不便(比如從Controler類跟蹤到Service類時,只能跳轉到接口類的相應函數,還要再按一次復雜的熱鍵才跳轉到實現類)
??? 連Martin Flower都說,強制每個類都分離接口和實現是過猶不及。只在有多個獨立實現,或者需要消除對實現類的依賴時,才需要分離接口。
????3.1 DAO被強制用接口的原因
??? Spring IOC本身是不會強制基于接口的,但DAO類一般要使用Spring的聲明式事務機制,而聲明式的事務機制是使用Spring AOP來實現的。Spring AOP的實現機制包括動態代理和Cgilib2,其中Spring AOP默認使用的Java動態代理是必須基于接口,所以就要求基于接口了。
????
??? 3.2 解決方法
??? 那就讓Spring AOP改用CGLib2,生成目標類的子類吧,我們只要指定使用聲明式事務的FactoryBean使用CGLib的方式來實現AOP,就可以不基于接口編程了。
??? 指定的方式為設置proxyTargetClass為true。如下:
<bean?class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
id="baseService"???abstract="true">
??<property?name="transactionManager"?ref="transactionManager"/>
??<property?name="proxyTargetClass"?value="true"/>


</bean>
???? 又因為這些Service Bean都是單例,效率應該不受影響。
??? 4.總結
??? 對比Appfuse里面的5個類,我的Model層里只有VO作為純數據載體,Manager類放商業方法。有人說這樣太簡單了,但一個應用,要劃成幾個JSP,一個Controller,一個Manager,一個VO,對我來說已經足夠復雜,再要往上架墻疊屋,恕不奉陪,起碼在我的項目范圍里不需要。(但有很多項目是需要的,神佑世人)
??? 后記:迫于世人的壓力,SpringSide還是把DAO和Service層分開了,但依然堅持不搞那么多接口。
Struts與Webwork的扇子請跳過本篇。
??? MVC不就是把M、V、C分開么?至唯物樸素的做法是兩個JSP一個負責View,一個負責Controller,再加一個負責Model的Java Bean,已經可以工作得很好,那時候一切都很簡單。
??? 而現在為了一些不是本質的功能,冒出這么多非標準的Web框架,實在讓人一陣郁悶。像Ruby On Rails那樣簡捷開發,可用可不用,而且沒有太多的限制需要學習的,比如Webwork這型還可以考慮。但像Struts那樣越用框架越麻煩,或者像Tapestry那樣有嚴重自閉傾向,額上鑿著"高手專用玩具"的,用在團隊里就是不負責任的行為了。
????so,我的MVC方案是使用Spring MVC的Controller接口,寫最普通的JavaBean作為Controller,本質就和當年拿JSP作Controller差不多,但擁有了Spring IOC的特性。
????之所以用這么消極的選擇標準,是因為覺得這一代MVC框架離重回RAD時代的標準還很遠,注定了只是一段短暫的,過渡的技術,不值得投資太多精力和團隊學習成本。
1. 原理???? Spring MVC按植物分類學屬于Martin Flower〈企業應用模式〉里的靜態配置型Front Controler,使用DispatchServlet截獲所有*.do的請求,按照xml文件的配置,調用對應的Command對象的handleRequest(request,response)函數,同時進行依賴對象的注入。
???? 我們的Controller層,就是實現handleRequest(request,response)函數的普通JavaBean。
2. 優勢
???? Spring MVC與struts相比的優勢:
?????一是它的Controller有著從松到緊的類層次結構,用戶可以選擇實現只有一個HandleRequest()函數的接口,也可以使用它有很多回調函數的SimpleFormController類。
?????二是不需要Form Bean,也不需要Tapestry那所謂面向對象的頁面對象,對于深怕類膨脹,改一個東西要動N個地方的人最適合不過。
?????三是不需要強XML配置文件,宣告式編程是好的,但如果強制成框架,什么都要在xml里面宣告,寫的時候繁瑣,看的時候也要代碼配置兩邊看才能明白就比較麻煩了。
?
?????那Webwork呢?沒有實戰過,不過因為對MVC框架所求就不多,單用Spring MVC的Controller已經可以滿足需求,就不多搞一套Webwork來給團隊設坎,還有給日后維護,spring,ww2之間的版本升級添麻煩了。真有什么需要添加的,Spring MVC源代碼量很少,很容易掌控和擴展。
?
3.化簡
3.1. 直接implement Controller,實現handleRequest()函數
??????首先,simple form controller非我所好,一點都不simple。所以有時我會直接implement Controller接口。這個接口的唯一函數是供Front Controller調用的handleRequest(request,response)。
????? 如果需要application對象,比如想用application.getRealPath()時,就要extends webApplicationObjectSupport。
3.2.每個Controler負責一組相關的action
?????? 我是堅決支持一個Controler負責多個action的,一個Controler一個action就像一個function一個類一樣無聊。所以我用最傳統的方式,用URL參數如msg="insert"把一組相關action交給一個Controler控制。ROR與制作中的Groovy On Rails都是這種模式,Spring也有MultiActionController支持。
?????? 以上三者都是把URL參數直接反射為Controller的函數,而
Stripes的設計可用annotation標注url action到響應函數的映射。
????我的取舍很簡單,反正Spring沒有任何強制,我只在可能需要不重新編譯而改變某些東西的時候,才把東西放在xml里動態注入。jsp路徑之類的就統統收回到controller里面定義.
?
3.4.Data Binder
?????? Data Binder是Controller的必有環節,對于Spring提供的DataBinder,照理完全可用,唯一不爽是對象如果有內嵌對象,如訂單對象里面包含了Customer對象,Spring需要你先自行創建了Customer對象并把它賦給了Order對象,才可能實現order.customer.customer_no這樣的綁定。我偷懶,又拿Jakarta BeanUtils出來自己做了一個Binder。
3.5.提取基類
????? 最后還是忍不住提取了一個基類,負責MultiAction和其他一些簡便的方法。Sprnig的MultiActionController做得太死,規定所有函數的第1,2個參數必須是request和response,不懂動態的,溫柔的進行參數注入。
??????
???? ?經過化簡再化簡,已經是很簡單一個Java Bean ,任誰都可以輕松上手,即使某年某月技術的大潮把現在所有MVC框架都淹沒了,也不至于沒人識得維護。
人生像個舞臺,請良家少女離開。
????同樣的,Freemarker和Velocity愛好者請跳過本篇。與棄用webwork而單用Spring MVC Controller接口的理由一樣,
Freemarker本來是一樣好東西,還跨界支持jsp?的taglib,而且得到了WebWork的全力支持,但為了它的非標準化,用戶數量與IDE的缺乏,在View層我們還是使用了
保守但人人會用,IDE友好的JSP2.0 配合JSTL。
??? 對于B/S結構的企業應用軟件來說,基本的頁面不外兩種,一種是填Form的,一種是DataGrid 數據列表管理的,再配合一些css, js, ajax的效果,就是View層要關注的東西了。
1. JSP 2.0的EL代替<c:out>JSP2.0可以直接把EL寫在html部分,而不必動用<c:out>節點后,老實說,JSP2.0+JSTL達到的頁面效果,已不比Velocity相差多少了。
<p>{goods.name}</p>?
代替
<p><c:out?value="{goods.name}"/></p>
(除了EL里面不能調用goods的函數,sun那幫老頑固始終堅持JSTL只能用于數據顯示,不能進行數據操作,所以不能調用bean的get/set外的方法)
?2. 最懶的form 數據綁定
??? Spring少得可憐的幾個tag基本上是雞肋,完全可以不要。 而Spring開發中的那些Simple Form tag又還沒有發布。Spring的Tag主要用來把VO的值綁到input框上。但是,和Struts一樣,需要逐個Input框綁定,而且語法極度冗長,遇到select框還要自己進行處理.....典型的Spring Sample頁面讓人一陣頭暈.
??? 而jodd的form tag給了我們懶人一個懶得多的方法,只要在<form>兩頭用<jodd:form bean="myVO"></jodd:form>包住,里面的所有input框,select框,checkBox...統統自動被綁定了,這么簡單的事情,真不明白struts,spring為什么不用,為了不必要的靈活性么?
<form>
<jodd:form?bean="human">
<input?type="text"?name="name">
<input?type="radiobox"?name="sex"?value="man">
<select?name="age">
??<option?value="20">20</option>
??<option?value="30">30</option>
</select>
</jodd:form>
</form>?
不過,jodd有個致命弱點是不能綁定內嵌對象的值。比如Order(訂單)對象里有個Customer(顧客)對象,jodd就不能像 struts,spring一樣用如下語法綁定:
<input?name="customer.customerNo">這是因為它的beanUtils比Jakata Common弱,用了一個錯誤的思路的緣故。 動用beanUtils修改一下就可以了,修改后的源碼可以在這里下載。
3. DataGrid數據列表
DisplayTag和ValueList都屬于這種形式的Tag Library。但最近出現的Extreme Table是真正的killer,他本身功能強大不說,而且從一開始就想著如何讓別人進行擴展重載,比如Extend Attributes機制就是DisplayTag這樣的讓千人一面者不會預留。
4.css, java script, ajax
天下紛擾,沒有什么特別想講想推薦的,愛誰誰吧。Buffalo, DWR, Scriptaculous, Prototype, AjaxTags, AjaxAnywhere, Rico, Dojo, JSON-RPC,看著名字就頭痛。