一個計算機專業學生幾年的編程經驗彙總(超經典)10-11Spirng Hibernate

Java雜談(十)--Spring

筆者最近比較忙,一邊在實習一邊在尋找明年畢業更好的工作,不過論壇裏的朋友非常支持小弟繼續寫,今天是週末,泡上一杯咖啡,繼續與大家分享J2ee部分的學習經驗。今天的主題是目前很流行也很好的一個開源框架-Spring。

引用《Spring2.0技術手冊》上的一段話:
Spring的核心是個輕量級容器,它是實現IoC容器和非侵入性的框架,並提供AOP概念的實現方式;提供對持久層、事務的支持;提供MVC Web框架的實現,並對於一些常用的企業服務API提供一致的模型封裝,是一個全方位的應用程序框架,除此之外,對於現存的各種框架,Spring也提供了與它們相整合的方案。
接下來筆者先談談自己的一些理解吧,Spring框架的發起者之前一本很著名的書名字大概是《J2ee Development without EJB》,他提倡用輕量級的組件代替重量級的EJB。筆者還沒有看完那本著作,只閱讀了部分章節。其中有一點分析覺得是很有道理的:

EJB裏在服務器端有Web Container和EJB Container,從前的觀點是各層之間應該在物理上隔離,Web Container處理視圖功能、在EJB Container中處理業務邏輯功能、然後也是EBJ Container控制數據庫持久化。這樣的層次是很清晰,但是一個很嚴重的問題是Web Container和EJB Container畢竟是兩個不同的容器,它們之間要通信就得用的是RMI機制和JNDI服務,同樣都在服務端,卻物理上隔離,而且每次業務請求都要遠程調用,有沒有必要呢?看來並非隔離都是好的。

再看看輕量級和重量級的區別,筆者看過很多種說法,覺得最有道理的是輕量級代表是POJO + IoC,重量級的代表是Container + Factory。(EJB2.0是典型的重量級組件的技術)我們儘量使用輕量級的Pojo很好理解,意義就在於兼容性和可適應性,移植不需要改變原來的代碼。而Ioc與Factory比起來,Ioc的優點是更大的靈活性,通過配置可以控制很多注入的細節,而Factory模式,行爲是相對比較封閉固定的,生產一個對象就必須接受它全部的特點,不管是否需要。其實輕量級和重量級都是相對的概念,使用資源更少、運行負載更小的自然就算輕量。

話題扯遠了,因爲Spring框架帶來了太多可以探討的地方。比如它的非侵入性:指的是它提供的框架實現可以讓程序員編程卻感覺不到框架的存在,這樣所寫的代碼並沒有和框架綁定在一起,可以隨時抽離出來,這也是Spring設計的目標。Spring是唯一可以做到真正的針對接口編程,處處都是接口,不依賴綁定任何實現類。同時,Spring還設計了自己的事務管理、對象管理和Model2 的MVC框架,還封裝了其他J2ee的服務在裏面,在實現上基本都在使用依賴注入和AOP的思想。由此我們大概可以看到Spring是一個什麼概念上的框架,代表了很多優秀思想,值得深入學習。筆者強調,學習並不是框架,而是框架代表的思想,就像我們當初學Struts一樣……

1.Spring MVC
關於IoC和AOP筆者在上篇已經稍微解釋過了,這裏先通過Spring的MVC框架來給大家探討一下Spring的特點吧。(畢竟大部分人已經很熟悉Struts了,對比一下吧)
衆所周知MVC的核心是控制器。類似Struts中的ActionServlet,Spring裏面前端控制器叫做DispatcherServlet。裏面充當Action的組件叫做Controller,返回的視圖層對象叫做ModelAndView,提交和返回都可能要經過過濾的組件叫做Interceptor。

讓我們看看一個從請求到返回的流程吧:
(1) 前臺Jsp或Html通過點擊submit,將數據裝入了request域
(2) 請求被Interceptor攔截下來,執行preHandler()方法出前置判斷
(3) 請求到達DispathcerServlet
(4) DispathcerServlet通過Handler Mapping來決定每個reuqest應該轉發給哪個後端控制器Controller
(5) 各式各樣的後端控制器Controller來處理請求,調用業務層對象來處理業務邏輯,然後返回一個ModelAndView對象
(6) 當Controller執行完畢,Interceptor會調用postHandle來做後置處理
(7) ModelAndView代表了呈現畫面是使用的Model數據對象和View對象,由於只能返回一個對象所有起了這個名字封裝這兩個對象。
(8) 由ViewResolver對象來解析每個返回的ModelAndView對象應該呈現到哪一個視圖(Jsp/Html等)中(包括Exception Resolver)
(9) 當View繪製完成之後Interceptor又會跳出來執行它的afterCompletion方法做善後處理。當然Interceptor的行爲完全是配置的而不是強制的。


這樣一個完整的流程就這樣結束了,個人感覺Spring的MVC框架稍顯複雜,不像Struts-1那麼容易上手。不管是Controller、Model、ViewRosovler、Handle Mapping還是View,Spring MVC框架都已經爲你提供了多種實現,想最大程度的減少程序員的編碼,增加框架的適用性。大家有興趣可以繼續深入研究哈!

2.Spring AOP
記得最初筆者請教他人Spring是一個什麼東西的時候,每個人都會提到AOP這個詞語。筆者在上一篇已經解釋過AOP基本原理,這次來跟大家說說Spring的AOP編程吧。不同的AOP框架會有其對AOP概念不同的實現方式,主要的差別在於所提供的Pointcut、Aspects的豐富程度,以及它們如何被織入應用程序、代理的方式等等。先熟悉一下AOP中的幾個重要概念:
(1) Cross-cutting:橫切,說白了就是需要統一處理的集合
(2) Aspects:將散落各處的橫切收集起來,設計成各個獨立可重用的對象稱爲Aspects。
(3) Advice: 對橫切的具體實現,即等待插入一段邏輯。
(4) Joinpoint:Advice插入流程的時機點。
(5) Pointcut: 用於選擇Joinpoint的程序結構,可以通過Annotation或者XML實現。
(6) Weave: Advice被應用至對象之上的過程稱之爲織入,有編譯期、類加載期、運行期三種時間點策略。

如果你採用實現接口的方式,Spring會在執行時期適用java的動態代理,如果不實現接口,Spring會使用CGLIB產生代理類。AOP的概念很大很泛,而Spring只使用了其中的部分特性,畢竟Spring的目標是輕量級框架,比如它只支持對Method的Joinpoint,而不支持對Field的Joinpoint,理由是爲了封裝性。

其實我們可以把概念看得簡單一點,AOP的目的是減少冗餘代碼,增強對較大項目的全局監控。Spring利用AOP可以規定一個集合和一套規則,在這個集合裏所有的方法被invoke即調用的時候,都必須按照那套規則走一遍。那麼首先對其中10個方法都要用到的處理代碼就只用寫一遍,如果是這10個方法來了就織入這段代碼;其次,按照規則,也許所有的牽扯某個模塊的方法調用的時候,我都需要做日誌或者進行驗證,那麼我只要立足於這個集合的入口和出口,管他從哪裏來去哪裏,都能被有效的監控。我監控的可能不止是某個方法單獨的行爲,我還可以加入對流程控制的監控規則。例如是論壇,我規定註冊了才能登錄,而登錄後才能發帖回帖下資源,於是所有這類流程都會被收集到我眼皮地下通過。

PS:筆者最近忙於找工作的事,沒有太多經歷在論壇跟大家整理自己的筆記。最近也只是接觸Spring的MVC比較多,對於Spring的其他特性,還沒有更多的去實踐,所以僅僅是泛泛而談,只是介紹一個印象罷了。還是那句話,我們學習一個框架不是如何使用,而是它所帶來的優秀的思想和理念,這比如何使用這個框架更有意義得多
Java雜談(十一)?ORM

這是最後一篇Java雜談了,以ORM框架的談論收尾,也算是把J2ee的最後一方面給涵蓋到了,之所以這麼晚才總結出ORM這方面,一是筆者這兩週比較忙,另一方面也想善始善終,仔細的先自己好好研究一下ORM框架技術,不想草率的敷衍了事。

其實J2ee的規範指南里面就已經包括了一些對象持久化技術,例如JDO(Java Data Object)就是Java對象持久化的新規範,一個用於存取某種數據倉庫中的對象的標準化API,提供了透明的對象存儲,對開發人員來說,存儲數據對象完全不需要額外的代碼(如JDBC API的使用)。這些繁瑣的工作已經轉移到JDO產品提供商身上,使開發人員解脫出來,從而集中時間和精力在業務邏輯上。另外,JDO很靈活,因爲它可以在任何數據底層上運行。JDBC只是面向關係數據庫(RDBMS)JDO更通用,提供到任何數據底層的存儲功能,比如關係數據庫、文件、XML以及對象數據庫(ODBMS)等等,使得應用可移植性更強。我們如果要理解對象持久化技術,首先要問自己一個問題:爲什麼傳統的JDBC來持久化不再能滿足大家的需求了呢?

筆者認爲最好是能用JDBC真正編寫過程序了才能真正體會ORM的好處,同樣的道理,真正拿Servlet/Jsp做過項目了才能體會到Struts、 Spring等框架的方便之處。很幸運的是筆者這兩者都曾經經歷過,用混亂的內嵌Java代碼的Jsp加Servlet轉發寫過完整的Web項目,也用 JDBC搭建過一個完整C/S項目的後臺。所以現在接觸到新框架才更能體會它們思想和實現的優越之處,回顧從前的代碼,真是醜陋不堪啊。^_^

回到正題,我們來研究一下爲什麼要從JDBC發展到ORM。簡單來說,傳統的JDBC要花大量的重複代碼在初始化數據庫連接上,每次增刪改查都要獲得 Connection對象,初始化Statement,執行得到ResultSet再封裝成自己的List或者Object,這樣造成了在每個數據訪問方法中都含有大量冗餘重複的代碼,考慮到安全性的話,還要加上大量的事務控制和log記錄。雖然我們學習了設計模式之後,可以自己定義Factory來幫助減少一部分重複的代碼,但是仍然無法避免冗餘的問題。其次,隨着OO思想深入人心,連典型的過程化語言Perl等都冠冕堂皇的加上了OO的外殼,何況是 Java中繁雜的數據庫訪問持久化技術呢?強調面向對象編程的結果就是找到一個橋樑,使得關係型數據庫存儲的數據能準確的映射到Java的對象上,然後針對Java對象來設計對象和方法,如果我們把數據庫的Table當作Class,Record當作Instance的話,就可以完全用面向對象的思想來編寫數據層的代碼。於是乎,Object Relationship Mapping的概念開始普遍受到重視,儘管很早很早就已經有人提出來了。

缺點我們已經大概清楚了,那麼如何改進呢?對症下藥,首先我們要解決的是如何從Data Schema準備完美的映射到Object Schema,另外要提供對數據庫連接對象生命週期的管理,對事務不同粒度的控制和考慮到擴展性後提供對XML、Properties等可配置化的文件的支持。到目前爲止,有很多框架和技術在嘗試着這樣做。例如似乎是封裝管理得過了頭的EJB、很早就出現目前已經不在開發和升級了的Apache OJB、首先支持Manual SQL的iBATIS,還有公認非常優秀的Hibernate等等。在分別介紹它們之前,我還想反覆強調這些框架都在試圖做什麼:

畢竟Java Object和數據庫的每一條Record還是有很大的區別,就是類型上來說,DB是沒有Boolean類型的。而Java也不得不用封裝類(Integer、Double等)爲了能映射上數據庫中爲null的情況,畢竟Primitive類型是沒有null值的。還有一個比較明顯的問題是,數據庫有主鍵和外鍵,而Java中仍然只能通過基本類型來對應字段值而已,無法規定Unique等特徵,更別提外鍵約束、事務控制和級聯操作了。另外,通過Java Object預設某Field值去取數據庫記錄,是否在這樣的記錄也是不能保證的。真的要設計到完全映射的話,Java的Static被所有對象共享的變量怎麼辦?在數據庫中如何表現出來……
我們能看到大量的問題像一座座大山橫在那些框架設計者們面前,他們並不是沒有解決辦法,而是從不同的角度去考慮,會得到很多不同的解決方案,問題是應該採取哪一種呢?甚至只有等到真正設計出來了投入生產使用了,才能印證出當初的設想是否真的能爲項目開發帶來更多的益處。筆者引用一份文檔中提到一個健壯的持久化框架應該具有的特點:
A robust persistence layer should support----
1. Several types of persistence mechanism
2. Full encapsulation of the persistence mechanism.
3. Multi-object actions
4. Transactions Control
5. Extensibility
6. Object identifiers
7. Cursors: logical connection to the persistence mechanism
8. Proxies: commonly used when the results of a query are to be displayed in a list
9. Records: avoid the overhead of converting database records to objects and then back to records
10. Multi architecture
11. Various database version and/or vendors
12. Multiple connections
13. Native and non-native drivers
14. Structured query language queries(SQL)

現在來簡短的介紹一下筆者用過的一些持久化框架和技術,之所以前面強調那麼多共通的知識,是希望大家不要盲從流行框架,一定要把握它的本質和卓越的思想好在哪裏。
1. Apache OJB
OJB代表Apache Object Relational Bridge,是Apache開發的一個數據庫持久型框架。它是基於J2ee規範指南下的持久型框架技術而設計開發的,例如實現了ODMG 3.0規範的API,實現了JDO規範的API, 核心實現是Persistence Broker API。OJB使用XML文件來實現映射並動態的在Metadata layer聽過一個Meta-Object-Protocol(MOP)來改變底層數據的行爲。更高級的特點包括對象緩存機制、鎖管理機制、 Virtual 代理、事務隔離性級別等等。舉個OJB Mapping的簡單例子ojb-repository.xml:

<class-descriptor class=”com.ant.Employee” table=”EMPLOYEE”>
<field-descriptor name=”id” column=”ID”
jdbc-type=”INTEGER” primarykey=”true” autoincrement=”true”/>

<field-descriptor name=”name” column=”NAME” jdbc-type=”VARCHAR”/>
</class-descrptor>

<class-descriptor class=”com.ant.Executive” table=”EXECUTIVE”>
<field-descriptor name=”id” column=”ID”
jdbc-type=”INTEGER” primarykey=”true” autoincrement=”true”/>

<field-descriptor name=”department” column=”DEPARTMENT” jdbc-type=”VARCHAR”/>

<reference-descriptor name=”super” class-ref=”com.ant.Employee”>
<foreignkey field-ref=”id”/>
</reference-descriptor>
</class-descrptor>

2. iBATIS
iBATIS最大的特點就是允許用戶自己定義SQL來組配Bean的屬性。因爲它的SQL語句是直接寫入XML文件中去的,所以可以最大程度上利用到 SQL語法本身能控制的全部特性,同時也能允許你使用特定數據庫服務器的額外特性,並不侷限於類似SQL92這樣的標準,它最大的缺點是不支持枚舉類型的持久化,即把枚舉類型的幾個對象屬性拼成與數據庫一個字段例如VARCHAR對應的行爲。這裏也舉一個Mapping文件的例子sqlMap.xml:
<sqlMap>
<typeAlias type=”com.ant.Test” alias=”test”/>

<resultMap class=”test” id=”result”>
<result property=”testId” column=”TestId”/>
<result property=”name” column=”Name”/>
<result property=”date” column=”Date”/>
</resultMap>

<select id=”getTestById” resultMap=”result” parameterClass=”int”>
select * from Test where TestId=#value#
</select>

<update id=”updateTest” parameterClass=”test”>
Update Tests set Name=#name#, Date=”date” where TestId=#testId#
</update>
</sqlMap>

3. Hibernate
Hibernate無疑是應用最廣泛最受歡迎的持久型框架,它生成的SQL語句是非常優秀。雖然一度因爲不能支持手工SQL而性能受到侷限,但隨着新一代 Hibernate 3.x推出,很多缺點都被改進,Hibernate也因此變得更加通用而時尚。同樣先看一個Mapping文件的例子customer.hbm.xml來有一個大概印象:

<hibernate-mapping>
<class name=”com.ant.Customer” table=”Customers”>
<id name=”customerId” column=”CustomerId” type=”int” unsaved-value=”0”>
<generator class=”sequence”>
<param name=”sequence”> Customers_CustomerId_Seq </param>
</generator>
</id>

<property name=”firstName” column=”FirstName”/>
<property name=”lastName” column=”LastName”/>

<set name=”addresses” outer-join=”true”>
<key column=”Customer”/>
<one-to-many class=”com.ant.Address”/>
</set>


</class>

</hibernate-mapping>

Hibernate有很多顯著的特性,最突出的就是它有自己的查詢語言叫做HQL,在HQL中select from的不是Table而是類名,一方面更加面向對象,另外一方面通過在hibernate.cfg.xml中配置Dialect爲HQL可以使得整個後臺與數據庫脫離耦合,因爲不管用那種數據庫我都是基於HQL來查詢,Hibernate框架負責幫我最終轉換成特定數據庫裏的SQL語句。另外 Hibernate在Object-Caching這方面也做得相當出色,它同時管理兩個級別的緩存,當數據被第一次取出後,真正使用的時候對象被放在一級緩存管理,這個時候任何改動都會影響到數據庫;而空閒時候會把對象放在二級緩存管理,雖然這個時候與數據庫字段能對應上但未綁定在一起,改動不會影響到數據庫的記錄,主要目的是爲了在重複讀取的時候更快的拿到數據而不用再次請求連接對象。其實關於這種緩存的設計建議大家研究一下Oracle的存儲機制(原理是相通的),Oracle犧牲了空間換來時間依賴於很健壯的緩存算法來保證最優的企業級數據庫訪問速率。

以上是一些Mapping的例子,真正在Java代碼中使用多半是繼承各個框架中默認的Dao實現類,然後可以通過Id來查找對象,或者通過 Example來查找,更流行的是更具Criteria查找對象。Criteria是完全封裝了SQL條件查詢語法的一個工具類,任何一個查詢條件都可以在Criteria中找到方法與之對應,這樣可以在Java代碼級別實現SQL的完全控制。另外,現在許多ORM框架的最新版本隨着JDk 5.0加入Annotation特性都開始支持用XDoclet來自動根據Annotation來生成XML配置文件了。

筆者不可能詳細的講解每一個框架,也許更多的人在用Hibernate,筆者是從OJB開始接觸ORM技術的,它很原始卻更容易讓人理解從JDBC到 ORM的過渡。更多的細節是可以從官方文檔和書籍中學到的,但我們應該更加看中它們設計思想的來源和閃光點,不是盲從它們的使用方法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章