1. 程式人生 > >spring基礎總結,比較全面 侵立刪

spring基礎總結,比較全面 侵立刪

目前很多公司的架構,從Struts2遷移到了SpringMVC。你有想過為什麼不使用Servlet+JSP來構建Java web專案,而是採用SpringMVC呢?

既然這樣,我們從源頭說起。Struts2的源頭其實也是Servlet。Servlet的作用是接收瀏覽器傳給服務端的請求(request),並將服務端處理完的響應(response)返回給使用者的瀏覽器,瀏覽器和服務端之間通過http協議進行溝通,其過程是瀏覽器根據使用者的選擇將相關資訊按http協議報文的規範組裝請求的http報文,報文通過網路傳輸到指定的伺服器,伺服器通過特定的web容器接收這個報文資訊,例如:tomcat,jetty,jboss這樣的web容器,web容器會將http報文解析出來,如果是使用者請求,最終解析出來的報文資訊會用一個request物件儲存起來,服務端使用這個request做完相應的處理後,服務端程式將結果資訊封裝到response物件裡,然後將response物件交給web容器,web容器則把這個response物件轉變為http協議的報文,並將報文回傳給瀏覽器,瀏覽器最後解析這個響應報文,將最終結果展示給使用者。

  Web容器創造了servlet介面,servlet介面就是開發人員自己實現業務邏輯的地方,程式設計師開發servlet就好比做填空題,而填空題的語境或者說上下文提示就是由request和response物件,但是javaEE規範裡的servlet介面很簡單,就三個方法init,service和destory,但是這個介面太籠統,所以規範裡還提供了一個HttpServlet類,這個類根據http請求型別提供了doGet,doPost等方法,servlet介面最大的特點就是根據http協議的特點進行定義,因此做servlet開發時候如果使用者對http協議特點不是特別熟悉,都會碰到或多或少令人迷惑的問題,特別是碰到一些複雜特殊的請求時候:例如檔案上傳,返回特殊的檔案格式到瀏覽器,這時候使用servlet開發就不是很方便了,servlet開發還有個問題可能大家常常被忽視,就是請求的資料的型別轉化,http協議傳輸都是文字形式,到了web容器解析後也是文字型別,如果碰到貨幣,數字,日期這樣的型別需要我們根據實際情況進行轉化,如果頁面傳送的資訊非常多,我們就不得不做大量型別轉化,這種工作沒有什麼技術含量,是個體力活而且很容易導致程式錯誤。同時java的企業開發都是圍繞javabean進行,型別轉化好的資料還要封裝到對應的javabean裡,這種轉來轉去的事情對於專案開發絕對不是什麼好事情,所以古老的struts1為這種問題找到了一種解決方案,就是定義了一個DTO物件(資料傳輸物件),專門負責做這樣的事情,不過到了struts2,整個替代servlet的action本身就是一個javabean。

  Java的企業開發一個技術特點就是使用javabean進行的,struts2的特點之一就是它替代servlet的操作類就是一個典型的javabean,首先struts2框架將頁面傳輸的資料進行型別轉化和封裝後將請求資訊封裝到了這個javabean的屬性裡,這樣我們開發web程式時候就省去了煩心的型別轉化和封裝的問題,前面我講到傳統的servlet是根據http協議進行定義的,它會按你請求方式(post還是get方式)來處理使用者的請求,但是對於一名程式開發人員而言,一個請求,具體到一個url,其實對於服務端而言就是服務端對外提供的一個功能,或者說是服務端對外的一個動作,如果我們使用servlet開發程式我們就得把http的動作轉化為具體的業務動作,這就讓程式開發變得繁瑣,增強了開發的難度,所以struts2替代servlet的javabean就遮蔽了servlet裡http的請求方式和具體業務動作轉化的問題,javabean裡的每一個方法都可以和每一個url請求一一對應,這必然減輕了開發的難度問題。

  Servlet另一個作用就是構造response物件,讓頁面獲得正確的響應,其實現代的瀏覽器是一個多媒體工具,文字,圖片,視屏等等東西都可以在瀏覽器裡顯示,資源的不同就會導致http響應報文的差別,如果我們使用servlet開發就要根據資源的不同在java程式裡用硬編碼的形式處理,這樣的程式很難複用,而且如果程式設計師對某種資源的處理理解不到位,就會導致問題的出現,struts2通過配置檔案的形式將這樣的邏輯從java程式裡剝離出來,使用配置的方式進行統一管理,這個做法和spring的AOP方式類似,這樣就讓結果處理方式更加統一,更加利於管理,同時也提升了程式的健壯性以及降低了開發的難度。

  Servlet在MVC開發模式裡就是其中C層即控制層,控制層就像俄羅斯的雙頭鷹(一個頭向東看一個頭向西看)一樣,一個頭向M層模型層看,一個頭向V層檢視層看,模型層也是用java編寫的,控制層也屬於服務端語言開發,所以M層和C層的溝通沒有天然的障礙,但是和V層檢視層就不一樣了,這是一個跨語言的溝通,對於瀏覽器,它只懂得html,javascript和css,瀏覽器是理解不了java這種語言的東西,但是要讓服務端的東西能被瀏覽器理解接受,我們就必須得把服務端的響應資訊放到頁面裡,因此就需要一個技術把java的資訊轉化到html頁面裡,這就是javaEE規範裡提供了jsp技術,jsp其實是一種服務端技術而非客戶端技術,不過它看起來似乎更像html技術,最早的jsp開發裡都是直接將java程式碼寫到頁面裡,這種壞處誰都知道,之後javaEE規範提供了自定義標籤技術,使用一種類似html標籤的方式來解析java程式碼,struts2框架提供了一整套完整的自定義標籤技術,這似乎聽起來不算啥,但是它的作用非凡,因為自定義標籤之所以叫自定義就是每個人都可以自己來定義,如果沒有一個規範必然產生混亂,而且一套完善的自定義標籤是個系統工程,一套完整的自定義標籤相當於我們在自己定義一套新的開發語言,做程式的人聽到這個一定就會明白開發一套完整的自定義標籤的工作量和開發難度都是難以想象的,而且自定義標籤都是和控制層緊密相連,其難度又會增加一個維度,所以struts2提供的自定義標籤對於業務開發帶來的將是質的飛越。

  Servlet裡還有兩個重要的技術:監聽器和過濾器,對於監聽器在web開發裡使用的場景比較少,都是一些十分特別的情況才會使用,大部分web開發裡可以忽略它的使用,我們用的最多的監聽器可能就是對ServletContext建立和銷燬的監聽器,ServletContext是整個web應用的全域性物件,它和Web應用的生命週期繫結在一起,因此使用這個監聽器對Web應用的全域性資訊進行初始化和銷燬操作,例如spring容器的初始化操作。比較有意思的是過濾器,在struts2裡有個攔截器,它們的作用相同都是用來攔截請求的,因為攔截器是struts2的特有功能,在struts2裡使用攔截器自然比使用過濾器更順手,其實攔截器所用的技術比過濾器更加先進,因為攔截器使用了反射技術,因此攔截器攔截的面更大,控制請求的能力更強,它能完成的任務也會更加的豐富多彩。

  在我第一次接觸struts2時候,有人告訴我struts設計的一個目的就是想遮蔽在控制層裡操作request和response物件,因為這兩個http協議的兒子會造成web開發裡思路的混亂,但是我在實際開發裡卻經常不自覺的使用這兩個物件。而且本人做前端開發非常喜歡使用ajax,使用ajax技術時候我就很討厭struts2的自定義標籤,我更加喜歡在頁面裡用javascript技術處理各種資訊,最終struts2在我眼裡就是一個servlet的變體,因此曾經有段時間我常常在想是不是可以拋棄struts2,直接用servlet,因為struts2裡用到了太多反射機制,特別是使用註解做配置(註解是用反射實現的),在java裡反射的執行效率是非常低的,直接使用servlet一定能提升web應用的執行效率。其實這個倒很難做到,因為當時我沒法在servlet裡靈活的運用spring技術。

  ^_^  ^_^ ^_^ ^_^ ^_^  ^_^ ^_^ ^_^ ^_^  ^_^ ^_^ ^_^ ^_^  ^_^ ^_^ ^_^ ^_^  ^_^ ^_^ ^_^ ^_^  ^_^ ^_^ ^_^ ^_^  ^_^ ^_^ ^_^ ^_^  ^_^ ^_^ ^_^ 

 說完Servlet+jsp技術到Struts2技術的過渡。接下來談談Spring。

  spring技術可以說是java企業開發裡最重要的技術,不過真的理解spring的作用和意義還真是一件麻煩的事情,很多人對spring理解其實都是停留在使用階段(例如:宣告式事務很好用等等),當今的spring技術生態環境裡可謂是蔚為壯觀,spring已經包羅永珍,它的內容之多完全不亞於它的本源java語言了,而spring這麼大的框都是建立在ioc和aop技術之上,只有深入理解了這兩個技術我們才能明白為什麼spring這個框能裝的下那麼多東西了。

  首先是ioc,ioc技術第一個解釋叫做控制反轉,它還有個解釋就是依賴注入,這兩個名字很難從字面理解,但是當你理解它的原理後就會發現它們的描述是何等準確。Ioc技術的本質就是構建物件的技術換句話說就是將一個類例項化成物件的技術,在java裡實例化類通過new關鍵字進行的,每次new一個類都會產生一個新的例項物件,這麼做視乎很浪費,有時這種浪費還挺危險,因為在程式開發時候我們常常只需要某個類永遠只能產生一個的例項物件這個時候就得使用單例模式,此外在設計模式裡還可以通過工廠方式產生物件,使用過spring的人看到上面的文字就知道了,spring裡bean的定義就和上面的內容一一對應,scope屬性single產生單例物件,prototype產生新物件,bean還可以通過工廠方式產生物件,可以說spring的bean就是製造物件的工具。面向物件程式設計裡物件相當於顯示生活中的一個實體,例如我們有個物件作用是完成打獵的操作,那麼打獵這個物件內部包含兩個輔助物件:人和槍,只有人和槍賦予了打獵這個物件,那麼打獵物件才能完成打獵的操作,但是構建一個人和槍的物件並不是看起來那麼簡單,這裡以槍為例,要創造一把槍我們需要金屬,需要機床,需要子彈,而機床和子彈又是兩個新物件,這些物件一個個相互巢狀相互關聯,大夥試想下如果我們在java程式碼裡構建一個槍的物件那是何其的複雜,假如我們要構造的不是簡單的槍物件而是更加複雜的航空母艦,那麼構造這個物件的成本之高是讓人難以想象的,怎麼來消除這種物件相互巢狀相互依賴的關係了?spring提供了一種方式,這種方式就是spring提供一個容器,我們在xml檔案裡定義各個物件的依賴關係,由容器完成物件的構建,當我們java程式碼裡需要使用某個例項的時候就可以從容器裡獲取,那麼物件的構建操作就被spring容器接管,所以它被稱為控制反轉,控制反轉的意思就是本來屬於java程式裡構建物件的功能交由容器接管,依賴注入就是當程式要使用某個物件時候,容器會把它注入到程式裡,這就叫做依賴注入。在java開發裡我們想使用某個類提供的功能,有兩種方式,一種就是構造一個新的類,新的類繼承該類,另一種方式則是將某個類定義在新類裡,那麼兩個類之間就建立一種關聯關係,spring的ioc容器就是實現了這種關聯關係(記住不是繼承關係哦),那麼某個類要被賦予到新類有哪些辦法了?一般只有兩種:一種就是通過建構函式,一種就是通過setXXX方式,這也是spring容器使用到了兩種標準的注入方式。

  不管是上面說的繼承方式,還是關聯方式其實都是增強目標物件能力的開發手段,在設計模式裡有一種代理模式,代理模式將繼承模式和關聯模式結合在一起使用,代理模式就是繼承模式和關聯模式的綜合體,不過這個綜合體的作用倒不是解決物件注入的問題,而是為具體操作物件找到一個保姆或者是祕書,這就和小說裡的二號首長一樣,這個二號首長對外代表了具體的例項物件,例項物件的入口和出口都是通過這個二號首長,因為具體的例項物件是一號首長,一號首長是要幹大事的,所以一些事務性,重複性的工作例如泡茶,安排車子,這樣的工作是不用勞煩一號首長的大駕,而是二號首長幫忙解決的,這就是aop的思想,aop解決程式開發裡事務性,和核心業務無關的問題,但這些問題對於業務場景的實現是很有必要的,在實際開發裡aop也是節省程式碼的一種方式。

  Spring的核心技術的作用本質就是一個溝通機制,spring總是盡全力的讓溝通的雙方資訊暢通,同時降低雙方的溝通成本,在現實機構裡一個善於溝通的人肯定是該公司的領導,很會溝通的領導能調動起各種資源的積極性,善於溝通的領導就會做到海納百川,讓各種不同人追隨他,所以當今的spring就是一個大框,什麼都可以往裡裝。Spring很像銀行,它不能直接創造物質財富,但是一切資源都要通過它進行流通,它能控制經濟發展的走向,回到程式的世界,spring的作用是被標榜為程式之間的解耦,spring能降低不同模組之間的耦合度,原因就是在程式開發裡不同模組之間資訊的溝通是通過物件傳遞完成的,而物件能否順利傳遞就是要合理的構建好物件,而管理好物件的構建方式就能管理好物件傳遞,這就是spring給系統架構設計帶來的好處。

 ^_^  ^_^  ^_^  ^_^  ^_^  ^_^  ^_^  ^_^ ^_^  ^_^  ^_^  ^_^  ^_^  ^_^  ^_^  ^_^ ^_^  ^_^  ^_^  ^_^  ^_^  ^_^  ^_^  ^_^ ^_^  ^_^ ^_^ ^_^ ^_^

 說到Spring, Spring的事務你懂嗎?

  什麼是事務?為什麼事務要管理?什麼是Spring事務?事務就是對一系列的資料庫操作(比如插入多條資料)進行統一的提交或回滾操作,如果插入成功,那麼一起成功,如果中間有一條出現異常,那麼回滾之前的所有操作。這樣可以防止出現髒資料,防止資料庫資料出現問題。開發中為了避免這種情況一般都會進行事務管理。在JDBC中,是通過Connection物件進行事務管理的,預設是自動提交事務,可以手工將自動提交關閉,通過commit方法進行提交,rollback方法進行回滾,如果不提交,則資料不會真正的插入到資料庫中。Hibernate中則是通過Transaction進行事務管理,處理方法與JDBC中類似。Spring中也有自己的事務管理機制,一般是使用TransactionMananger進行管理,可以通過Spring的注入完成此功能。

  我通俗的理解如下:spring只是控制資料庫的事務提交和回滾,藉助於java的反射機制,在事務控制的方法(通常是service層的方法)前後獲取事務開啟session,然後執行你的資料操作,如果你的方法內有異常被丟擲,spring會捕獲異常並回滾你在這個方法內所有的資料操作,如果成功則提交所有的資料,最後spring會幫你關閉需要關閉的東西。所以spring想要做的是,要程式設計師專注於寫邏輯,不需要關心資料庫何時開啟和關閉連線。

 再說的通俗點兒:事務,對於一件事,對了就提交,錯了就回滾,什麼時候回滾,都是事務要做的事情。具體的操作由spring 配置來管理(同時你也可以脫離框架,自己寫事務管理方法)。

  使用Spring事務的優點?

複製程式碼

  在SSH框假中Spring充當了管理容器的角色。我們都知道Hibernate用來做持久層,因為它將JDBC做了一個良好的封裝,程式設計師在與資料庫進行互動時可以不用書寫大量的SQL語句。Struts是用來做應用層的,他它負責呼叫業務邏輯serivce層。所以SSH框架的流程大致是:Jsp頁面----Struts------Service(業務邏輯處理類)---Hibernate(左到右)。struts負責控制Service(業務邏輯處理類),從而控制了Service的生命週期,這樣層與層之間的依賴很強,屬於耦合。這時,使用spring框架就起到了控制Action物件(Strus中的)和Service類的作用,兩者之間的關係就鬆散了,Spring的Ioc機制(控制反轉和依賴注入)正是用在此處。
 
  Spring的Ioc(控制反轉和依賴注入) 
控制反轉:就是由容器控制程式之間的(依賴)關係,而非傳統實現中,由程式程式碼直接操控 
依賴注入:元件之間的依賴關係由容器在執行期決定 ,由容器動態的將某種依賴關係注入到元件之中。 
從上面我們不難看出:從頭到尾Action僅僅是充當了Service的控制工具,這些具體的業務方法是怎樣實現的,他根本就不會管,也不會問,他只要知道這些業務實現類所提供的方法介面就可以了。而在以往單獨使用Struts框架的時候,所有的業務方法類的生命週期,甚至是一些業務流程都是由Action來控制的。層與層之間耦合性太緊密了,既降低了資料訪問的效率又使業務邏輯看起來很複雜,程式碼量也很多。Spring容器控制所有Action物件和業務邏輯類的生命週期,由於上層不再控制下層的生命週期,層與層之間實現了完全脫耦,使程式執行起來效率更高,維護起來也方便。
 
  使用Spring的第二個好處(AOP應用): 
事務的處理: 
在以往的JDBCTemplate中事務提交成功,異常處理都是通過Try/Catch 來完成,而在Spring中。Spring容器集成了TransactionTemplate,她封裝了所有對事務處理的功能,包括異常時事務回滾,操作成功時資料提交等複雜業務功能。這都是由Spring容器來管理,大大減少了程式設計師的程式碼量,也對事務有了很好的管理控制。Hibernate中也有對事務的管理,hibernate中事務管理是通過SessionFactory建立和維護Session來完成。而Spring對SessionFactory配置也進行了整合,不需要在通過hibernate.cfg.xml來對SessionaFactory進行設定。這樣的話就可以很好的利用Sping對事務管理強大功能。避免了每次對資料操作都要現獲得Session例項來啟動事務/提交/回滾事務還有繁瑣的Try/Catch操作。這些也就是Spring中的AOP(面向切面程式設計)機制很好的應用。一方面使開發業務邏輯更清晰、專業分工更加容易進行。另一方面就是應用Spirng AOP隔離降低了程式的耦合性使我們可以在不同的應用中將各個切面結合起來使用大大提高了程式碼重用度。有利於程式碼重用,特別是Dao程式碼的重用。事務往往和業務規則緊密關聯。當業務邏輯發生改變,意味著dao的大幅度改動。系統規模達到一定程度,修改風險相當大。Spring的好處是不更改現有的dao,僅需對現有的service bean進行配置就達到事務效果了。同時,把事務統一在service層,系統結構更清晰。
為什麼說風險風大?
Spring對於事務的配置有兩種方式:第一種,使用xml形式,第二種,使用註解的形式。
基於XMl方式: 優點:可以在後期維護的時候適當的調整事務管理模式,並且只要遵循一定的命名規範,可以讓程式設計師不必關心事務。
        缺點:系統越龐大,xml配置就越大。
基於註解方式:優點:配置比較方便,程式設計師只要在service層程式碼設定即可以實現。不需要知道系統需要多少個bean,交給容器來注入就好了。
        缺點:當你要修改或刪除一個bean的時候,你無法確定到底有多少個其他的bean依賴於這個bean。(解決方法:需要有嚴格的開發文件,在修改實現時儘可能繼續遵守相應的介面避免使其他依賴於此的bean不可用)

複製程式碼

  在我們用SSH開發專案的時候,我們一般都是將事務設定在Service層 那麼當我們呼叫Service層的一個方法的時候它能夠保證我們的這個方法中執行的所有的對資料庫的更新操作保持在一個事務中,在事務層裡面呼叫的這些方法要麼全部成功,要麼全部失敗。那麼事務的傳播特性也是從這裡說起的。 如果你在你的Service層的這個方法中,除了呼叫了Dao層的方法之外,還呼叫了本類的其他的Service方法,那麼在呼叫其他的 Service方法的時候,這個事務是怎麼規定的呢,我必須保證我在我方法裡掉用的這個方法與我本身的方法處在同一個事務中,否則如何保證事物的一致性。事務的傳播特性就是解決這個問題的,“事務是會傳播的”在Spring中有針對傳播特性的多種配置我們大多數情況下只用其中的一種:PROPGATION_REQUIRED:這個配置項的意思是說當我呼叫service層的方法的時候開啟一個事務(具體呼叫那一層的方法開始建立事務,要看你的aop的配置),那麼在呼叫這個service層裡面的其他的方法的時候,如果當前方法產生了事務就用當前方法產生的事務,否則就建立一個新的事務。這個工作是由Spring來幫助我們完成的。 以前沒有Spring幫助我們完成事務的時候我們必須自己手動的控制事務,例如當我們專案中僅僅使用hibernate,而沒有整合進 spring的時候,我們在一個service層中呼叫其他的業務邏輯方法,為了保證事物必須也要把當前的hibernate session傳遞到下一個方法中,或者採用ThreadLocal的方法,將session傳遞給下一個方法,其實都是一個目的。現在這個工作由 spring來幫助我們完成,就可以讓我們更加的專注於我們的業務邏輯。而不用去關心事務的問題。預設情況下當發生RuntimeException的情況下,事務才會回滾,所以要注意一下。如果你在程式發生錯誤的情況下,有自己的異常處理機制定義自己的Exception,必須從RuntimeException類繼承,這樣事務才會回滾!

  補充文字來說一下上面的關於Spring的一個問題。基於xml和基於註解,當然了它們都有優缺點。我們通俗的說,是這樣的。先來回顧一下傳統上是如何配置 Bean 並完成 Bean 之間依賴關係的建立。下面是 3 個類,它們分別是 Office、Car 和 Boss,這 3 個類需要在 Spring 容器中配置為 Bean。

複製程式碼

//  Office.java
public class Office {
    private String officeNo =”001”;

    //省略 get/setter

    @Override
    public String toString() {
        return "officeNo:" + officeNo;
    }
}

複製程式碼

複製程式碼

//  Car.java
public class Car {
    private String brand;
    private double price;

    // 省略 get/setter

    @Override
    public String toString() {
        return "brand:" + brand + "," + "price:" + price;
    }
}

複製程式碼

複製程式碼

//  Boss.java
public class Boss {
    private Car car;
    private Office office;

    // 省略 get/setter

    @Override
    public String toString() {
        return "car:" + car + "\n" + "office:" + office;
    }
}

複製程式碼

我們在 Spring 容器中將 Office 和 Car 宣告為 Bean,並注入到 Boss Bean 中:下面是使用傳統 XML 完成這個工作的配置檔案 beans.xml:

複製程式碼

//  bean.xml將以上三個類配置成bean。
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean id="boss" class="com.baobaotao.Boss">
        <property name="car" ref="car"/>
        <property name="office" ref="office" />
    </bean>
    <bean id="office" class="com.baobaotao.Office">
        <property name="officeNo" value="002"/>
    </bean>
    <bean id="car" class="com.baobaotao.Car" scope="singleton">
        <property name="brand" value=" 紅旗 CA72"/>
        <property name="price" value="2000"/>
    </bean>
</beans>

複製程式碼

複製程式碼

//  當我們執行這段程式碼時,控制檯將正確打印出boss的資訊。這說明 Spring 容器已經正確完成了 Bean 建立和裝配的工作。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnoIoCTest {

    public static void main(String[] args) {
        String[] locations = {"beans.xml"};
        ApplicationContext ctx = 
            new ClassPathXmlApplicationContext(locations);
        Boss boss = (Boss) ctx.getBean("boss");
        System.out.println(boss);
    }
}

複製程式碼

我們知道 Spring 2.5 中引入了 @Autowired 註釋,它可以對類成員變數、方法及建構函式進行標註,完成自動裝配的工作。來看一下使用@Autowired 進行成員變數自動注入的程式碼: 

複製程式碼

// Autowired是自動裝配的意思
import org.springframework.beans.factory.annotation.Autowired;

public class Boss {

    @Autowired
    private Car car;

    @Autowired
    private Office office;
}

// Spring 通過一個 BeanPostProcessor@Autowired 進行解析,所以要讓@Autowired 起作用必須事先在 Spring 容器中宣告 AutowiredAnnotationBeanPostProcessor Bean。
// 讓系統認識@Autowired,讓@Autowired註釋工作起來。

複製程式碼

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <!-- 該 BeanPostProcessor 將自動起作用,對標註 @Autowired 的 Bean 進行自動注入 -->
    <bean class="org.springframework.beans.factory.annotation.
        AutowiredAnnotationBeanPostProcessor"/>

    <!-- 移除 boss Bean 的屬性注入配置的資訊 -->
    <bean id="boss" class="com.baobaotao.Boss"/>          // 這裡面沒配置哦
 
    <bean id="office" class="com.baobaotao.Office">
        <property name="officeNo" value="001"/>
    </bean>
    <bean id="car" class="com.baobaotao.Car" scope="singleton">
        <property name="brand" value=" 紅旗 CA72"/>
        <property name="price" value="2000"/>
    </bean>
</beans>

複製程式碼

這樣,當 Spring 容器啟動時,AutowiredAnnotationBeanPostProcessor 將掃描 Spring 容器中所有 Bean,當發現 Bean 中擁有@Autowired 註釋時就找到和其匹配(預設按型別匹配)的 Bean,並注入到對應的地方中去。按照上面的配置,Spring 將直接採用 Java 反射機制對 Boss 中的

car 和 office 這兩個私有成員變數進行自動注入。所以對成員變數使用@Autowired 後,您大可將它們的 setter 方法(setCar() 和 setOffice())從 Boss 中刪除。當然,您也可以通過 @Autowired 對方法或建構函式進行標註,來看下面的程式碼:

複製程式碼

public class Boss {
    private Car car;
    private Office office;

     @Autowired
    public void setCar(Car car) {
        this.car = car;
    }
 
    @Autowired
    public void setOffice(Office office) {
        this.office = office;
    }
}

複製程式碼

這時,@Autowired 將查詢被標註的方法的入參型別的 Bean,並呼叫方法自動注入這些 Bean。而下面的使用方法則對建構函式進行標註:

 Spring IOC三種注入方式(介面注入、setter注入、構造器注入)

public class Boss {
    private Car car;
    private Office office;
 
    @Autowired
    public Boss(Car car ,Office office){
        this.car = car;
        this.office = office ;
    }
}
// 由於 Boss() 建構函式有兩個入參,分別是 caroffice@Autowired 將分別尋找和它們型別匹配的 Bean,將它們作為Boss(Car car ,Office office) 的入參來建立 Boss Bean。

在預設情況下使用 @Autowired 註釋進行自動注入時,Spring 容器中匹配的候選 Bean 數目必須有且僅有一個。當找不到一個匹配的 Bean 時,Spring 容器將丟擲BeanCreationException 異常,並指出必須至少擁有一個匹配的 Bean。我們可以來做一個實驗:

複製程式碼

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">
 
    <bean class="org.springframework.beans.factory.annotation.
        AutowiredAnnotationBeanPostProcessor"/> 

    <bean id="boss" class="com.baobaotao.Boss"/>

    <!-- 將 office Bean 註釋掉 -->
    <!-- <bean id="office" class="com.baobaotao.Office">             //然後你想,當多個bean之間互相依賴的時候,是不是維護起來很麻煩啊。
    <property name="officeNo" value="001"/>
    </bean>-->

    <bean id="car" class="com.baobaotao.Car" scope="singleton">
        <property name="brand" value=" 紅旗 CA72"/>
        <property name="price" value="2000"/>
    </bean>
</beans>

由於 office Bean 被註釋掉了,所以 Spring 容器中將沒有型別為 Office 的 Bean 了,而 Boss 的office 屬性標註了 @Autowired,當啟動 Spring 容器時,異常就產生了。當不能確定 Spring 容器中一定擁有某個類的 Bean 時,可以在需要自動注入該類 Bean 的地方可以使用 @Autowired(required = false),這等於告訴 Spring:在找不到匹配 Bean 時也不報錯。來看一下具體的例子:

複製程式碼

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;

public class Boss {

    private Car car;
    private Office office;

    @Autowired
    public void setCar(Car car) {
        this.car = car;
    }
    @Autowired(required = false)
    public void setOffice(Office office) {
        this.office = office;
    }
}

複製程式碼

當然,一般情況下,使用 @Autowired 的地方都是需要注入 Bean 的,使用了自動注入而又允許不注入的情況一般僅會在開發期或測試期碰到(如為了快速啟動 Spring 容器,僅引入一些模組的 Spring 配置檔案),所以@Autowired(required = false) 會很少用到。和找不到一個型別匹配 Bean 相反的一個錯誤是:如果 Spring 容器中擁有多個候選 Bean,Spring 容器在啟動時也會丟擲 BeanCreationException 異常。來看下面的例子:

複製程式碼

// 在 beans.xml 中配置兩個 Office 型別的 Bean
<bean id="office" class="com.baobaotao.Office">
    <property name="officeNo" value="001"/>
</bean>
<bean id="office2" class="com.baobaotao.Office">
    <property name="officeNo" value="001"/>
</bean>

複製程式碼

我們在 Spring 容器中配置了兩個型別為 Office 型別的 Bean,當對 Boss 的 office 成員變數進行自動注入時,Spring 容器將無法確定到底要用哪一個 Bean,因此異常發生了。Spring 允許我們通過 @Qualifier 註釋指定注入 Bean 的名稱,這樣歧義就消除了,可以通過下面的方法解決異常:

@Autowired
public void setOffice(@Qualifier("office")Office office) {
    this.office = office;
}

@Qualifier("office") 中的 office 是 Bean 的名稱,所以 @Autowired 和@Qualifier 結合使用時,自動注入的策略就從 byType 轉變成 byName 了。@Autowired 可以對成員變數、方法以及建構函式進行註釋,而@Qualifier 的標註物件是成員變數、方法入參、建構函式入參。正是由於註釋物件的不同,所以 Spring 不將 @Autowired 和@Qualifier 統一成一個註釋類。下面是對成員變數和建構函式入參進行註釋的程式碼:對成員變數進行註釋:

複製程式碼

public class Boss {
    @Autowired
    private Car car;
 
    @Autowired
    @Qualifier("office")
    private Office office;
}

複製程式碼

複製程式碼

 我們面試中提到Spring框架經常被問到的一塊兒是Spring的事務,它優於struts2,優於hibernate。那麼我們現在就詳細的說說這個東西。

// 事務的特性:原子性、一致性、隔離性、永續性。

常常問的:1. Spring事務的傳播特性; 2. Spring事務的隔離機制。

 我們先來探討一下資料庫事務的隔離級別:

複製程式碼

事務的(ACID)特性是由關係資料庫管理系統(RDBMS,資料庫系統)來實現的。資料庫管理系統採用日誌來保證事務的原子性、一致性和永續性。日誌記錄了事務對資料庫所做的更新,如果某個事務在執行過程中發生錯誤,就可以根據日誌,撤銷事務對資料庫已做的更新,使資料庫退回到執行事務前的初始狀態。資料庫管理系統採用鎖機制來實現事務的隔離性。當多個事務同時更新資料庫中相同的資料時,只允許持有鎖的事務能更新該資料,其他事務必須等待,直到前一個事務釋放了鎖,其他事務才有機會更新該資料。

幻讀 是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的資料進行了修改,比如這種修改涉及到表中的“全部資料行”。同時,第二個事務也修改這個表中的資料,這種修改是向表中插入“一行新資料”。那麼,以後就會發生操作第一個事務的使用者發現表中還有沒有修改的資料行,就好象發生了幻覺一樣.一般解決幻讀的方法是增加範圍鎖RangeS,鎖定檢鎖範圍為只讀,這樣就避免了幻讀。

髒讀 就是指當一個事務正在訪問資料,並且對資料進行了修改,而這種修改還沒有提交到資料庫中,這時,另外一個事務也訪問這個資料,然後使用了這個資料。因為這個資料是還沒有提交的資料,那麼另外一個事務讀到的這個資料是髒資料,依據髒資料所做的操作可能是不正確的。

 資料庫系統有四個隔離級別。對資料庫使用何種隔離級別要審慎分析,因為維護一個最高的隔離級別雖然會防止資料的出錯,但是卻導致了並行度的損失,以及導致死鎖出現的可能性增加。然而,降低隔離級別,卻會引起一些難以發現的bug。

  1. 序列化 Serializable, 新增範圍鎖(比如表鎖,頁鎖等,關於range lock,我也沒有很深入的研究),直到transaction A結束。以此阻止其它trasaction B對此範圍內的insert,update等操作。幻讀,髒讀,不可重複讀等問題都不會發生。

  2. 可重複讀 repeatable read, 對於讀出的記錄,新增共享鎖直到transaction A結束。其它transaction B對這個記錄的試圖修改會一直等待直到trasaction A結束。InnoDB 預設級別可能發生的問題:當執行一個範圍查詢時,可能會發生幻讀。

  3. 提交讀 read commited, 在trasaction A中讀取資料時對記錄新增共享鎖,但讀取結束立即釋放。其它transaction B對這個記錄的試圖修改會一直等待直到A中的讀取過程結束,而不需要整個trasaction A的結束。所以,在trasaction A的不同階段對同一記錄的讀取結果可能是不同的。

       可能發生的問題:不可重複讀。

  4. 未提交讀 read uncommited, 不新增共享鎖。所以其它trasaction B可以在trasaction A對記錄的讀取過程中修改同一記錄,可能會導致A讀取的資料是一個被破壞的或者說不完整不正確的資料。另外,在trasaction A中可以讀取到trasaction B(未提交)中修改的資料。比如trasaction     B對R記錄修改了,但未提交。此時,在Trasaction A中讀取R記錄,讀出的是被B修改過的資料。可能發生的問題:髒讀。

複製程式碼

 那麼Spring的事務隔離機制和Spring事務的傳播屬性呢,Spring的隔離級別我們知道目的是為了防止幻讀和髒讀。Spring的事務傳播屬性似乎好難理解。。接下來,先解釋下什麼叫做事務的傳播屬性:

複製程式碼

我們都知道事務的概念,那麼事務的傳播特性是什麼呢?(先著重介紹傳播特性的概念,關於傳播特性的相關配置稍後再介紹)
背景:當我們用SSH開發專案的時候,我們一般都是將事務設定在Service層 那麼當我們呼叫Service層的一個方法的時候它能夠保證我們的這個方法中執行的所有的對資料庫的更新操作保持在一個事務中,在事務層裡面呼叫的這些方法要麼全部成功,要麼全部失敗。那麼事務的傳播特性也是從這裡說起的。
場景:如果你在你的Service層的這個方法中,除了呼叫了Dao層的方法之外,還呼叫了本類的其他的Service方法,那麼在呼叫其他的Service方法的時候,這個事務是怎麼規定的呢,我必須保證我在我方法裡掉用的這個方法與我本身的方法處在同一個事務中,否則如何保證事物的一致性。事務的傳播特性就是解決這個問題的,“事務是會傳播的”在Spring中有針對傳播特性的多種配置我們大多數情況下只用其中的1種:PROPGATION_REQUIRED:這個配置項的意思是說當我呼叫service層的方法的時候開啟一個事務(具體呼叫那一層的方法開始建立事務,要看你的aop的配置),那麼在呼叫這個service層裡面的其他的方法的時候,如果當前方法產生了事務就用當前方法產生的事務,否則就建立一個新的事務。這個工作使由Spring來幫助我們完成的。
以前沒有Spring幫助我們完成事務的時候我們必須自己手動的控制事務,例如當我們專案中僅僅使用hibernate,而沒有整合進spring的時候,我們在一個service層中呼叫其他的業務邏輯方法,為了保證事物必須也要把當前的hibernate session傳遞到下一個方法中,或者採用ThreadLocal的方法,將session傳遞給下一個方法,其實都是一個目的。現在這個工作由spring來幫助我們完成,就可以讓我們更加的專注於我們的業務邏輯。而不用去關心事務的問題。預設情況下當發生RuntimeException的情況下,事務才會回滾,所以要注意一下如果你在程式發生錯誤的情況下,有自己的異常處理機制定義自己的Exception,必須從RuntimeException類繼承這樣事務才會回滾!

複製程式碼

好了,知道了事務的傳播特性之後,我們看看Spring提供的六種事務傳播Propagation特性:仔細感覺一下,六中Spring事務傳播機制 對稱美 

1. Propagation required, 如果當前沒有事務,就新建一個事務。
2. Propagation supports, 如果當前沒有事務,就以非事務方式執行。
3. Propagation mandatory, 如果當前沒有事務,就丟擲異常。
4. Propagation requires new, 如果當前存在事務,掛起當前事務,新建事務。
5. Propagation not supported, 如果當前存在事務,把當前事務掛起,以非事務方式執行。
6. Propagation never, 如果當前存在事務,丟擲異常。以非事務方式執行。

關於六種事務傳播機制的具體應用場景,參考: Spring事務傳播機制部落格

 瞭解了Spring事務的傳播特性,再來看看Spring事務的五種隔離級別isolation level。

複製程式碼

// 先再來回顧前面說到的三個概念
髒讀: 指當一個事務正在訪問資料,並且對資料進行了修改,而這種修改還沒有提交到資料庫中,這時,另外一個事務也訪問這個資料,然後使用了這個資料。因為這個資料是還沒有提交的資料, 那麼另外一個事務讀到的這個資料是髒資料,依據髒資料所做的操作可能是不正確的。
    
不可重複讀: 指在一個事務內,多次讀同一資料。在這個事務還沒有結束時,另外一個事務也訪問該同一資料。那麼,在第一個事務中的兩次讀資料之間,由於第二個事務的修改,那麼第一個事務兩次讀到的資料可能是不一樣的。這樣就發生了在一個事務內兩次讀到的資料是不一樣的,因此稱為是不可重複讀。
            
幻覺讀: 指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的資料進行了修改,這種修改涉及到表中的全部資料行。同時,第二個事務也修改這個表中的資料,這種修改是向表中插入一行新資料。那麼,以後就會發生操作第一個事務的使用者發現表中還有沒有修改的資料行,就好象發生了幻覺一樣。

複製程式碼

複製程式碼

// 再來看Spring事務的隔離級別isolation level:

 1. isolation default: 這是一個預設的隔離級別,使用資料庫預設的事務隔離級別.(一般情況下,使用這種)。另外四個與JDBC的隔離級別相對應.

 2. isolation read uncommited: 這個是事務最低的隔離級別,它允許令外一個事務可以看到這個事務未提交的資料。這種隔離級別會產生髒讀,不可重複讀和幻像讀。(trasaction B可以在trasaction A對記錄的讀取過程中修改同一記錄)
   產生髒讀、產生不重複讀、產生幻讀。

 3. isolation read commited: 保證一個事務修改的資料提交後才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的資料.(trasaction B提交後trasaction A才能讀取)
   避免髒讀、會產生不重複讀、會產生幻讀。

 4. isolation repeatable read: 這種事務隔離級別可以防止髒讀,不可重複讀。但是可能出現幻讀。它除了保證一個事務不能讀取另一個事務未提交的資料外,還保證了避免下面的情況產生(不可重複讀)。
   避免髒讀、避免不重複讀、會產生幻讀。

 5. isolation serializable.這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。 除了防止髒讀,不可重複讀外,還避免了幻讀。
   避免髒讀、避免不重複讀、避免幻讀。

複製程式碼

複製程式碼

  換種口吻說,宣告式事務。宣告式事務是Spring提供的對程式事務管理的方式之一。Sping的宣告式事務,就是在配置檔案中採用配置的方式對事務進行管理。Spring中的AOP即,是完成事務管理工作的。

總結一下我們出幾道面試題整合一下Spring3的知識。

1. Spring的工作原理:

1.客戶端請求提交到DispatcherServlet
2. 由DispatcherServlet控制器查詢一個或多個HandlerMapping,找到處理請求的Controller
3. DispatcherServlet將請求提交到Controller
4. Controller呼叫業務邏輯處理後,返回ModelAndView
5. DispatcherServlet查詢一個或多個ViewResoler檢視解析器,找到ModelAndView指定的檢視
6. 檢視負責將結果顯示到客戶端

 2. 為什麼要用Spring?

複製程式碼

1、Spring能很好的與各大框架進行整合
2、建立物件時,如果我們不用spring。需要用工廠模式來建立,這個spring相當於工廠模式已經幫我們做了建立物件的功能(IOC、依賴注入)。
3、在用Hibernate的時候,如果不用spring每次都要寫事務的提交程式碼,有了spring可以通過AOP幫助我們管理事務。
4、面向切面程式設計(AOP)在要記錄日誌的時候新增一條記錄後需要在資料裡同時新增一條新增成功了或失敗的記錄,那麼就可以用Spring的Aop來處理,雖然不用Aop也能做但是不用Spring的Aop就會寫很多重複的程式碼。

AOP 讓開發人員可以建立非行為性的關注點,稱為橫切關注點,並將它們插入到應用程式程式碼中。使用 AOP 後,公共服務 (比 如日誌、永續性、事務等)就可以分解成方面並應用到域物件上,同時不會增加域物件的物件模型的複雜性。IOC 允許建立一個可以構造物件的應用環境,然後向這些物件傳遞它們的協作物件。正如單詞 倒置 所表明的,IOC 就像反 過來的 JNDI。沒有使用一堆抽象工廠、服務定位器、單元素(singleton)和直接構造(straight construction),每一個物件都是用 其協作物件構造的。因此是由容器管理協作物件(collaborator)。Spring即使一個AOP框架,也是一IOC容器。 Spring 最好的地方是它有助於您替換物件。有了 Spring,只要用 JavaBean 屬性和配置檔案加入依賴性(協作物件)。然後可以很容易地在需要時替換具有類似介面的協作物件。 

複製程式碼

 3. 請你談談SSH的整合?

複製程式碼

請你談談SSH整合 
SSH:Struts(表示層)+Hibernate(持久層)+Spring(業務層)

a、Struts 
Struts是一個表示層框架,主要作用是介面展示,接收請求,分發請求。
b、Hibernate 
Hibernate是一個持久層框架,它只負責與關係資料庫的操作。
c、Spring 
Spring是一個業務層框架,是一個整合的框架,能夠很好地黏合表示層與持久層。

複製程式碼

4. 介紹一下Spring的事務管理?

介紹一下Spring的事務管理 
事務就是對一系列的資料庫操作(比如插入多條資料)進行統一的提交或回滾操作,如果插入成功,那麼一起成功,如果中間有一條出現異常,那麼回滾之前的所有操作。這樣可以防止出現髒資料,防止資料庫資料出現問題。開發中為了避免這種情況一般都會進行事務管理。Spring中也有自己的事務管理機制,一般是使用TransactionMananger進行管理,可以通過Spring的注入來完成此功能。

 5. 什麼是依賴注入,依賴注入的作用是什麼?

什麼是依賴注入,依賴注入的作用是什麼? 
IOC是一種思想,它能指導我們如何設計出鬆耦合、更優良的程式。傳統應用程式都是由我們在類內部主動建立依賴物件,從而導致類與類之間高耦合,難於測試. 有了IOC容器後,把建立和查詢依賴物件的控制權交給了容器,由容器進行注入組合物件,所以物件與物件之間是鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程式的整個體系結構變得非常靈活。

依賴注入的作用:減少類間耦合度,避免用new來建立物件。

 6.什麼是aop,aop的作用是什麼?

什麼是AOP,AOP的作用是什麼? 
AOP,面向切面程式設計,就是把可重用的功能提取出來,然後將這些通用功能在合適的時候織入到應用程式中,比如事務管理、許可權控制、日誌記錄、效能統計等。

AOP的作用
AOP並沒有幫助我們解決任何新的問題,它只是提供了一種更好的辦法,能夠用更少的工作量來解決現有的一些問題,使得系統更加健壯,可維護性更好。

7. Spring中的BeanFactory與ApplicationContext的作用有哪些?

1、BeanFactory負責讀取bean的配置檔案,管理bean的載入、例項化,維護bean之間的依賴關係,負責bean的生命週期。 
2、ApplicationContext除了提供上述BeanFactory所能提供的功能之外,還提供了更完整的框架功能: 
a. 國際化支援
b. 資源訪問
c. 事件傳遞

8.  Hibernate的工作原理?

複製程式碼

Hibernate工作原理及為什麼要用? 
原理: 
1.讀取並解析配置檔案 
2.讀取並解析對映資訊,建立SessionFactory 
3.開啟Sesssion 
4.建立事務Transation 
5.持久化操作 
6.提交事務 
7.關閉Session 
8.關閉SesstionFactory

複製程式碼

9. 為什麼要用hibernate?

// 為什麼要用: 
1. 對JDBC訪問資料庫的程式碼做了封裝,大大簡化了資料訪問層繁瑣的重複性程式碼。 
2. Hibernate是一個基於JDBC的主流持久化框架,是一個優秀的ORM實現。他很大程度的簡化DAO層的編碼工作 
3. hibernate使用Java反射機制,而不是位元組碼增強程式來實現透明性。 
4. hibernate的效能非常好,因為它是個輕量級框架。對映的靈活性很出色。它支援各種關係資料庫,從一對一到多對多的各種複雜關係。

 10. Hibernate如何延遲載入?

Hibernate是如何延遲載入? 
1. Hibernate2延遲載入實現:a)實體物件 b)集合(Collection) 
2. Hibernate3 提供了屬性的延遲載入功能 
當Hibernate在查詢資料的時候,資料並沒有存在與記憶體中,當程式真正對資料的操作時,物件才存在與記憶體中,就實現了延遲載入,他節省了伺服器的記憶體開銷,從而提高了伺服器的效能。

11. Hibernate怎麼樣實現類之間的關係?

Hibernate中怎樣實現類之間的關係?(如:一對多、多對多的關係)

類與類之間的關係主要體現在表與表之間的關係進行操作,它們都是對物件進行操作,我們程式中把所有的表與類都對映在一起,它們通過配置檔案中的many-to-one、one-to-many、many-to-many、

 12. 說下hibernate的快取機制。

複製程式碼

詳細說下hibernate的快取機制:


 // 為什麼要用hibernate快取:

Hibernate是一個持久層框架,經常訪問物理資料庫。為了降低應用程式對物理資料來源訪問的頻次,從而提高應用程式的執行效能。快取內的資料是對物理資料來源中的資料的複製,應用程式在執行時從快取讀寫資料,在特定的時刻或事件會同步快取和物理資料來源的資料。

 // Hibernate的快取原理是怎麼樣的?

Hibernate一級快取又稱為“Session的快取”。Session的快取是事務範圍的快取(Session物件的生命週期通常對應一個數據庫事務或者一個應用事務)。

Hibernate二級快取又稱為“SessionFactory的快取”。由於SessionFactory物件的生命週期和應用程式的整個過程對應,因此Hibernate二級快取是程序範圍或者叢集範圍的快取,有可能出現併發問題,因此需要採用適當的併發訪問策略,該策略為被快取的資料提供了事務隔離級別。第二級快取是可選的,是一個可配置的外掛,預設下SessionFactory不會啟用這個外掛。

   // 既然二級快取是程序級別的快取,那麼它適合快取什麼型別的資料呢?

      什麼樣的資料適合存放到第二級快取中?   
          1) 很少被修改的資料   
          2) 不是很重要的資料,允許出現偶爾併發的資料   
          3) 不會被併發訪問的資料   
          4) 常量資料   
  不適合存放到第二級快取的資料?   
          1) 經常被修改的資料   
          2) 絕對不允許出現併發訪問的資料,如財務資料,絕對不允許出現併發   
          3) 與其他應用共享的資料。

  // Session的延遲載入

  Session的延遲載入實現要解決兩個問題:正常關閉連線和確保請求中訪問的是同一個session。Hibernate session就是java.sql.Connection的一層高階封裝,一個session對應了一個Connection。http請求結束後正確的關閉session(過濾器實現了session的正常關閉);延遲載入必須保證是同一個session(session繫結在ThreadLocal)。

  // Hibernate查詢物件, 如何應用快取?

當Hibernate根據ID訪問資料物件的時候,首先從Session一級快取中查;查不到,如果配置了二級快取,那麼從二級快取中查;如果都查不到,再查詢資料庫,把結果按照ID放入到快取刪除、更新、增加資料的時候,同時更新快取。

複製程式碼

 接下來說說Spring事務的配置:

  Spring 如果沒有特殊說明,一般指是跟資料儲存有關的資料操作事務操作;對於資料持久操作的事務配置,一般有三個物件,資料來源(dataSouce),事務管理器(transactionManager),以及事務代理機制;Spring 提供了多種的底層資料來源實現,以及多種型別的事務管理器;所有的管理器都基於 Platform Transaction Manager 介面實現各自的事務策略;Spring 事務管理採用 AOP 切面代理技術實現,AOP 用於分隔關注點,保證事務的原子性,採用一定的技術 把該關注點 (weaving) 織入到 待完善的關注點上,實現單獨元件無法實現的功能,以解決面向物件程式設計在某些方式下難於實現的操作,更好的支援面向物件的開關原則(擴充套件開放,修改關閉)。

對於三部分:dataSource、transactionManager、事務代理機制。無論哪種配置方式,一般變化的都是代理機制部分。DataSource、TransactionManager這兩部分只是會根據資料訪問方式有所變化。比如使用Hibernate進行資料訪問時,DataSource實際為SessionFactory,TransactionManager的實現為HibernateTransactionManager

  那麼我們知道了事務有五種配置方式和三個物件,接下來說說它的層面:Spring的事務到底該給Dao配置還是給Service配置呢?Spring的事務為業務邏輯進行事務管理,保證業務邏輯上資料的原子性。事務根據專案性質來細分:事務可以設定到三個層面(dao層、service層和web層),第一:web層事務,這一般是針對那些安全性要求較高的系統來說的。例如電子商務網站。粒度小,一般系統用不著這麼細。第二:service層事務,這是一常見的事務劃分, 將事務設定在業務邏輯上,只要業務邏輯出錯或異常就事務回滾。粒度較小,一般推薦這種方式。第三:資料持久層資料務,也就是常說的資料庫事務。這種事務在安全性方面要求低。就是給一個簡單的增刪改之類的操作增加事務操作,粒度大。    

  Spring宣告式事務讓我們從複雜的事務處理中得到解脫。使得我們再也無需要去處理獲得連線、關閉連線、事務提交和回滾等這些操作。再也無需要我們在與事務相關的方法中處理大量的try…catch…finally程式碼。 
我們在使用Spring宣告式事務時,有一個非常重要的概念就是事務屬性。事務屬性通常由事務的傳播行為,事務的隔離級別,事務的超時值和事務只讀標誌組成。我們在進行事務劃分時,需要進行事務定義,也就是配置事務的屬性。給Service層配置事務,因為一個Service層方法操作可以關聯到多個DAO的操作。在Service層執行這些Dao操作,多DAO操作有失敗全部回滾,成功則全部提交。事務分為業務事務和系統事務,業務事務也就是業務邏輯上操作的一致性,系統事務自然就是指真正的資料庫事務,Spring配置事務的是為了什麼進行管理,當然是為業務邏輯進行事務管理,保證業務邏輯上資料的原子性;Dao層是什麼,資料訪問層,是不應該包含業務邏輯的,這就是和Service層的不同;Service層就是業務邏輯層,事務的管理就是為Service層上的保證。

複製程式碼

package com.bluesky.spring.dao;  
  
import java.util.List;  
  
import org.hibernate.SessionFactory;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;  
import org.springframework.stereotype.Component;  
  
import com.bluesky.spring.domain.User;  
  
@Transactional    // 看程式碼(關於事務的配置)
@Component("userDao")  
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {  
    public List<User> listUsers() {  
        return this.getSession().createQuery("from User").list();  
    }      
} 

// 在DAO層上加了註解 @Transactional ,這種申明