cmd

          導航

          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          統計

          常用鏈接

          留言簿(2)

          隨筆檔案

          文章分類

          文章檔案

          收藏夾

          other

          優秀blog

          優秀官方

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          簡化Spring

               人人都愛Spring加Hibernate。
              但Spring MVC+hibernate的Sample如Appfuse的代碼卻遠算不得最簡潔優美好讀,如果在自己的項目中繼續發揮我們最擅長的依樣畫葫蘆大法,美好愿望未必會實現。
               所以,Pramatic精神不滅。這個系列就是探尋最適合自己的Spring+Hibernate模式。

           I-配置文件簡化

               我厭倦一切配置文件繁重的框架。
               最好的情況是,框架提供極端靈活復雜的配置方式,但只在你需要的時候
           
               Spring提供了三種可能來簡化XML。隨著國內用戶水平的提高,這些基本的簡化技巧大家都已掌握,從Spring 1.2開始又有了小小的改進,<Spring XML: way simpler since 1.2>作了集大成的歸納

          1.1.autowire="byName" /"byType"

               假設Controller有一個屬性名為customerDAO,Spring就會在配置文件里查找有沒有名字為CustomerDAO的bean, 自動為Controller注入。
                如果bean有兩個屬性,一個想默認注入,一個想自定義,只要設定了autowire,然后顯式的聲明 那個想自定義的,就可以達到要求。這就應了需求,在需要特別配置的時候就提供配置,否則給我一個默認注入可以了。

               還有一個更懶的地方,在最最根部的<beans>節點寫一句default-autovwrie="byName",可以讓所有bean 都默認autowrie。
              不過Rod認為開發期可以這樣,但Production Server上不應該使用Autowire,大家自己來決定了。

          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 id="saleOrderController" class="com.itorgan.web.SaleOrderController" />

          2.DAO只剩下5行

          <bean id="saleOrderDAO" parent="baseDAOService" >
           
          <property name="target">
             
          <bean class="com.itorgan.lherp.dao.impl.SaleOrderDAOImpl" /> 
           
          </property>
          </bean>
          3.Spring 1.2后xml語法簡化

           
          最主要的簡化是把屬性值和引用bean從子節點變回了屬性值,對不喜歡autowire的兄弟比較有用。
           當然,如果value要CDATA的時候還是要用子節點。
          1.屬性值

            <property name="foo">
              
          <value>fooValue</value>
            </property>
            簡化為
            <property name="foo" value="fooValue"/>

          2.引用 bean
          <property name="foo">
            
          <ref bean="fooBean">
          </property>
          簡化為
          <property name="foo" value="fooBean"/>


          3. list如果只有一個值時,可以直接用value表示
           
          <property name="myFriendList">
           
          <list>
              
          <value>wuyu</value>
           
          </list>
          </property>
          簡化為
          <property name="myFriendList" value="wuyu"/>

             
            因為只有一個值時,List就退化為Object類型嘛,多體貼。

          4.使用Spring自帶的DTD使編輯器Smart.

              如果沒有用Eclipse的Spring插件,那至少也要使用spring自帶的dtd使XML編輯器smart一些,能夠自動為你生成屬性,判斷節點/屬性名稱有沒有拼錯等。

          5.還有更變態的簡化配置方法
              比如autoproxy,不過我覺得更簡化就不可控了,所以沒有采用。

          II-Model層簡化

              因為Spring自帶的sample離我們的實際項目很遠,所以官方一點的model層模式展現就靠Appfuse了。
              但Appfuse的model層總共有一個DAO接口、一個DAOImpl類、一個Service接口、一個ServiceImpl類、一個DataObject.....大概只有受慣了虐待的人才會欣然接受吧。
              另外,Domain-Driven逢初一、十五也會被拿出來討論一遍。
              其實無論什么模式,都不過是一種人為的劃分、抽象和封裝。只要在團隊里理解一致,自我感覺優雅就行了。在我的Model層里,DO和Manager一生一 旦包演全場,VO作為純數據載體,而Manager類放置商業方法,用getHibernateTemplate()直接訪問數據庫,不強制基于接口編 程......

              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類。
              二是一個商業過程里可能需要同時調用Manager甲乙丙丁的方法。 

                這些時候,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,對我來說已經足夠復雜,再要往上架墻疊屋,恕不奉陪,起碼在我的項目范圍里不需要。 (但有很多項目是需要的,神佑世人)

          III-Controller層簡化

              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到響應函數的映射。
                 3.3.xml宣告式編程的取舍 
              我的取舍很簡單,反正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框架都淹沒了,也不至于沒人識得維護。

          IV-View層簡化

              人生像個舞臺,請良家少女離開。
              同樣的,Freemarker和Velocity愛好者請跳過本篇。與棄用webwork而單用Spring MVC Controller接口的理由一樣,Freemarker本來是一樣好東西,還跨界支持jsp 的taglib,但為了它的非標準化,用戶數量與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, javascript, ajax
          天下紛擾,沒有什么特別想講想推薦的,愛誰誰吧。

          posted on 2006-03-07 11:38 靜夜思 閱讀(285) 評論(0)  編輯  收藏 所屬分類: 開源軟件

          主站蜘蛛池模板: 石台县| 聂拉木县| 雅江县| 大理市| 兰坪| 鲁山县| 西平县| 万载县| 遂宁市| 华亭县| 昌邑市| 宜城市| 陆河县| 北海市| 盐池县| 轮台县| 海原县| 玛纳斯县| 高碑店市| 读书| 黄龙县| 阳谷县| 温州市| 治多县| 千阳县| 辰溪县| 仪征市| 金沙县| 宜都市| 廊坊市| 太湖县| 沙河市| 滨州市| 班戈县| 诏安县| 汶上县| 黑水县| 大关县| 黔南| 大同市| 育儿|