spring

spring兩大核心——IOC,AOP(單例)

IOC:控制反轉依賴注入

AOP:面向切面

jdbc通過connection對象進行事務管理的,默認自動提交事務,通過commit方法進行提交,rollback方法進行回滾

hibernate通過transaction進行事務管理,處理方法與jdbc相似。通過創建SessionFactory和維護session來完成,

spring也有自己的事務管理機制,一般是transactionMananger進行管理,通過spring的注入完成此功能。

 

 

 

 

轉載自

http://www.cnblogs.com/RunForLove/p/4641672.html

http://www.360doc.com/content/17/0610/16/36916942_661644970.shtml

該文對於處於一定階段的開發人員來說有助於認清整個web開發和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 ,這種申明式事務方式,讓我們從複雜的事務處理中得到解脫,使得我們再也無需要去處理獲得連接、關閉連接、事務提交和回滾等這些操作。再也無需要我們在與事務相關的方法中處理大量的try…catch…finally代碼。

複製代碼

 

接下來,我們看看事務的定義

複製代碼

// TransactionDefinition    
public interface TransactionDefinition {    
      int getPropagationBehavior();    //方法 返回事務的傳播行爲,是否由一個活動事務來決定一個事務的調用。
      int getTimeout();     //方法 返回事務必須在多少秒內執行
      boolean isReadOnly();    //方法返回事務是否是隻讀的 
    int getIsolationLevel();    // 返回事務的隔離級別
} 

說說事務的隔離級別:在TransactionDefinition接口中定義了五個不同的事務隔離級別,ISOLATION_DEFAULT 這是一個默認的隔離級別,使用數據庫默認的事務隔離級別.另外四個與JDBC的隔離級別相對應。ISOLATION_READ_UNCOMMITTED 這是事務最低的隔離級別,它充許別外一個事務可以看到這個事務未提交的數據。這種隔離級別會產生髒讀,不可重複讀和幻像讀。 

複製代碼

例如: 
  Mary的原工資爲1000,財務人員將Mary的工資改爲了8000,但未提交事務 

Java代碼  

1. Connection con1 = getConnection();  

2. con.setAutoCommit(false);  

3. update employee set salary = 8000 where empId ="Mary";  


與此同時,Mary正在讀取自己的工資 

Java代碼  

1. Connection con2 = getConnection();  

2. select  salary from employee where empId ="Mary";  

3. con2.commit();  



Mary發現自己的工資變爲了8000,歡天喜地! 
而財務發現操作有誤,而回滾了事務,Mary的工資又變爲了1000 

Java代碼  

1. //con1  

2.   con1.rollback();  


像這樣,Mary記取的工資數8000是一個髒數據。 

複製代碼

 

 

ISOLATION_READ_COMMITTED  保證一個事務修改的數據提交後才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據。這種事務隔離級別可以避免髒讀出現,但是可能會出現不可重複讀和幻像讀。ISOLATION_REPEATABLE_READ  這種事務隔離級別可以防止髒讀,不可重複讀。但是可能出現幻像讀。它除了保證一個事務不能讀取另一個事務未提交的數據外,還保證了避免下面的情況產生(不可重複讀)。 

複製代碼

在事務1中,Mary 讀取了自己的工資爲1000,操作並沒有完成 
Java代碼  
1. con1 = getConnection();  

2. select salary from employee empId ="Mary";  

在事務2中,這時財務人員修改了Mary的工資爲2000,並提交了事務. 
Java代碼  
1. con2 = getConnection();  

2. update employee set salary = 2000;  

3. con2.commit();  

在事務1中,Mary 再次讀取自己的工資時,工資變爲了2000 
Java代碼  
1. //con1  

2. select salary from employee empId ="Mary";  

在一個事務中前後兩次讀取的結果並不致,導致了不可重複讀。 
使用ISOLATION_REPEATABLE_READ可以避免這種情況發生。 

複製代碼

 

 

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

複製代碼

@Transactional只能被應用到public方法上, 對於其它非public的方法,如果標記了@Transactional也不會報錯,但方法沒有事務功能。Spring使用聲明式事務處理,默認情況下,如果被註解的數據庫操作方法中發生了unchecked異常,所有的數據庫操作將rollback;如果發生的異常是checked異常,默認情況下數據庫操作還是會提交的。這種默認的行爲是可以改變的。事務詳細講解的文章

第一種方式,每個bean都有一個代理類。

複製代碼

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans">
    <!-- 數據源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://192.168.0.244:3306/test?useUnicode=true&amp;characterEncoding=UTF-8" />
        <property name="username" value="root" />
        <property name="password" value="root" />
        <!-- 連接池啓動時的初始值 -->
        <property name="initialSize" value="10" />
        <!-- 連接池的最大值 -->
        <property name="maxActive" value="10" />
        <!-- 最大空閒值.當經過一個高峯時間後,連接池可以慢慢將已經用不到的連接慢慢釋放一部分,一直減少到maxIdle爲止 -->
        <property name="maxIdle" value="20" />
        <!--  最小空閒值.當空閒的連接數少於閥值時,連接池就會預申請去一些連接,以免洪峯來時來不及申請 -->
        <property name="minIdle" value="10" />
        <property name="defaultAutoCommit" value="true" />
    </bean>
    <!-- 會話工廠 -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mappingLocations">
            <list>
                <value>classpath:/com/nms/entity/**/*.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect"> org.hibernate.dialect.MySQL5Dialect </prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
            </props>
        </property>
    </bean>    
    <!-- 定義事務管理器 -->  
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    <!-- 配置服務層 -->
    <bean id="userDaoAgency" class="com.dao.impl.UserDaoImpl">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>    
    <bean id="userDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">  
        <!-- 配置事務管理器 -->  
        <property name="transactionManager" ref="transactionManager" />     
        <property name="target" ref="userDaoAgency" />  
        <property name="proxyInterfaces" value="com.dao.UserDao" />
        <!-- 配置事務屬性 -->  
        <property name="transactionAttributes">  
            <props>  
                <prop key="*">PROPAGATION_REQUIRED</prop>
            </props>  
        </property>  
    </bean>
</beans>

複製代碼

第二種方式,所有bean共享一個代理類。

複製代碼

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <!-- 數據源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <!-- // 配置同上 -->
    </bean>
    <!-- 會話工廠 -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <!-- // 配置同上 -->
    </bean>
    <!-- 定義事務管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
     <!-- 定義事務 -->
    <bean id="base" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" lazy-init="true" abstract="true">
        <!-- 配置事務管理器 -->
        <property name="transactionManager" ref="transactionManager" />
        <!-- 配置事務屬性 -->
        <property name="transactionAttributes">
            <props>
                <prop key="*">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>
    <!-- 配置服務層 -->
    <bean id="userDao" class="com.dao.impl.UserDaoImpl">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    <!-- 代理對象 -->
    <bean id="userDaoAgency" parent="base">
        <property name="target" ref="userDao" />
    </bean>
</beans>

複製代碼

 第三種方式,使用攔截器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章