javascript
Spring发展历程总结
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
轉(zhuǎn)自與??https://www.cnblogs.com/RunForLove/p/4641672.html
目前很多公司的架構(gòu),從Struts2遷移到了SpringMVC。你有想過為什么不使用Servlet+JSP來構(gòu)建Java web項(xiàng)目,而是采用SpringMVC呢?
既然這樣,我們從源頭說起。Struts2的源頭其實(shí)也是Servlet。Servlet的作用是接收瀏覽器傳給服務(wù)端的請求(request),并將服務(wù)端處理完的響應(yīng)(response)返回給用戶的瀏覽器,瀏覽器和服務(wù)端之間通過http協(xié)議進(jìn)行溝通,其過程是瀏覽器根據(jù)用戶的選擇將相關(guān)信息按http協(xié)議報(bào)文的規(guī)范組裝請求的http報(bào)文,報(bào)文通過網(wǎng)絡(luò)傳輸?shù)街付ǖ姆?wù)器,服務(wù)器通過特定的web容器接收這個(gè)報(bào)文信息,例如:tomcat,jetty,jboss這樣的web容器,web容器會將http報(bào)文解析出來,如果是用戶請求,最終解析出來的報(bào)文信息會用一個(gè)request對象存儲起來,服務(wù)端使用這個(gè)request做完相應(yīng)的處理后,服務(wù)端程序?qū)⒔Y(jié)果信息封裝到response對象里,然后將response對象交給web容器,web容器則把這個(gè)response對象轉(zhuǎn)變?yōu)閔ttp協(xié)議的報(bào)文,并將報(bào)文回傳給瀏覽器,瀏覽器最后解析這個(gè)響應(yīng)報(bào)文,將最終結(jié)果展示給用戶。
Web容器創(chuàng)造了servlet接口,servlet接口就是開發(fā)人員自己實(shí)現(xiàn)業(yè)務(wù)邏輯的地方,程序員開發(fā)servlet就好比做填空題,而填空題的語境或者說上下文提示就是由request和response對象,但是javaEE規(guī)范里的servlet接口很簡單,就三個(gè)方法init,service和destory,但是這個(gè)接口太籠統(tǒng),所以規(guī)范里還提供了一個(gè)HttpServlet類,這個(gè)類根據(jù)http請求類型提供了doGet,doPost等方法,servlet接口最大的特點(diǎn)就是根據(jù)http協(xié)議的特點(diǎn)進(jìn)行定義,因此做servlet開發(fā)時(shí)候如果使用者對http協(xié)議特點(diǎn)不是特別熟悉,都會碰到或多或少令人迷惑的問題,特別是碰到一些復(fù)雜特殊的請求時(shí)候:例如文件上傳,返回特殊的文件格式到瀏覽器,這時(shí)候使用servlet開發(fā)就不是很方便了,servlet開發(fā)還有個(gè)問題可能大家常常被忽視,就是請求的數(shù)據(jù)的類型轉(zhuǎn)化,http協(xié)議傳輸都是文本形式,到了web容器解析后也是文本類型,如果碰到貨幣,數(shù)字,日期這樣的類型需要我們根據(jù)實(shí)際情況進(jìn)行轉(zhuǎn)化,如果頁面?zhèn)魉偷男畔⒎浅6?#xff0c;我們就不得不做大量類型轉(zhuǎn)化,這種工作沒有什么技術(shù)含量,是個(gè)體力活而且很容易導(dǎo)致程序錯(cuò)誤。同時(shí)java的企業(yè)開發(fā)都是圍繞javabean進(jìn)行,類型轉(zhuǎn)化好的數(shù)據(jù)還要封裝到對應(yīng)的javabean里,這種轉(zhuǎn)來轉(zhuǎn)去的事情對于項(xiàng)目開發(fā)絕對不是什么好事情,所以古老的struts1為這種問題找到了一種解決方案,就是定義了一個(gè)DTO對象(數(shù)據(jù)傳輸對象),專門負(fù)責(zé)做這樣的事情,不過到了struts2,整個(gè)替代servlet的action本身就是一個(gè)javabean。
Java的企業(yè)開發(fā)一個(gè)技術(shù)特點(diǎn)就是使用javabean進(jìn)行的,struts2的特點(diǎn)之一就是它替代servlet的操作類就是一個(gè)典型的javabean,首先struts2框架將頁面?zhèn)鬏數(shù)臄?shù)據(jù)進(jìn)行類型轉(zhuǎn)化和封裝后將請求信息封裝到了這個(gè)javabean的屬性里,這樣我們開發(fā)web程序時(shí)候就省去了煩心的類型轉(zhuǎn)化和封裝的問題,前面我講到傳統(tǒng)的servlet是根據(jù)http協(xié)議進(jìn)行定義的,它會按你請求方式(post還是get方式)來處理用戶的請求,但是對于一名程序開發(fā)人員而言,一個(gè)請求,具體到一個(gè)url,其實(shí)對于服務(wù)端而言就是服務(wù)端對外提供的一個(gè)功能,或者說是服務(wù)端對外的一個(gè)動(dòng)作,如果我們使用servlet開發(fā)程序我們就得把http的動(dòng)作轉(zhuǎn)化為具體的業(yè)務(wù)動(dòng)作,這就讓程序開發(fā)變得繁瑣,增強(qiáng)了開發(fā)的難度,所以struts2替代servlet的javabean就屏蔽了servlet里http的請求方式和具體業(yè)務(wù)動(dòng)作轉(zhuǎn)化的問題,javabean里的每一個(gè)方法都可以和每一個(gè)url請求一一對應(yīng),這必然減輕了開發(fā)的難度問題。
Servlet另一個(gè)作用就是構(gòu)造response對象,讓頁面獲得正確的響應(yīng),其實(shí)現(xiàn)代的瀏覽器是一個(gè)多媒體工具,文字,圖片,視屏等等東西都可以在瀏覽器里顯示,資源的不同就會導(dǎo)致http響應(yīng)報(bào)文的差別,如果我們使用servlet開發(fā)就要根據(jù)資源的不同在java程序里用硬編碼的形式處理,這樣的程序很難復(fù)用,而且如果程序員對某種資源的處理理解不到位,就會導(dǎo)致問題的出現(xiàn),struts2通過配置文件的形式將這樣的邏輯從java程序里剝離出來,使用配置的方式進(jìn)行統(tǒng)一管理,這個(gè)做法和spring的AOP方式類似,這樣就讓結(jié)果處理方式更加統(tǒng)一,更加利于管理,同時(shí)也提升了程序的健壯性以及降低了開發(fā)的難度。
Servlet在MVC開發(fā)模式里就是其中C層即控制層,控制層就像俄羅斯的雙頭鷹(一個(gè)頭向東看一個(gè)頭向西看)一樣,一個(gè)頭向M層模型層看,一個(gè)頭向V層視圖層看,模型層也是用java編寫的,控制層也屬于服務(wù)端語言開發(fā),所以M層和C層的溝通沒有天然的障礙,但是和V層視圖層就不一樣了,這是一個(gè)跨語言的溝通,對于瀏覽器,它只懂得html,javascript和css,瀏覽器是理解不了java這種語言的東西,但是要讓服務(wù)端的東西能被瀏覽器理解接受,我們就必須得把服務(wù)端的響應(yīng)信息放到頁面里,因此就需要一個(gè)技術(shù)把java的信息轉(zhuǎn)化到html頁面里,這就是javaEE規(guī)范里提供了jsp技術(shù),jsp其實(shí)是一種服務(wù)端技術(shù)而非客戶端技術(shù),不過它看起來似乎更像html技術(shù),最早的jsp開發(fā)里都是直接將java代碼寫到頁面里,這種壞處誰都知道,之后javaEE規(guī)范提供了自定義標(biāo)簽技術(shù),使用一種類似html標(biāo)簽的方式來解析java代碼,struts2框架提供了一整套完整的自定義標(biāo)簽技術(shù),這似乎聽起來不算啥,但是它的作用非凡,因?yàn)樽远x標(biāo)簽之所以叫自定義就是每個(gè)人都可以自己來定義,如果沒有一個(gè)規(guī)范必然產(chǎn)生混亂,而且一套完善的自定義標(biāo)簽是個(gè)系統(tǒng)工程,一套完整的自定義標(biāo)簽相當(dāng)于我們在自己定義一套新的開發(fā)語言,做程序的人聽到這個(gè)一定就會明白開發(fā)一套完整的自定義標(biāo)簽的工作量和開發(fā)難度都是難以想象的,而且自定義標(biāo)簽都是和控制層緊密相連,其難度又會增加一個(gè)維度,所以struts2提供的自定義標(biāo)簽對于業(yè)務(wù)開發(fā)帶來的將是質(zhì)的飛越。
Servlet里還有兩個(gè)重要的技術(shù):監(jiān)聽器和過濾器,對于監(jiān)聽器在web開發(fā)里使用的場景比較少,都是一些十分特別的情況才會使用,大部分web開發(fā)里可以忽略它的使用,我們用的最多的監(jiān)聽器可能就是對ServletContext創(chuàng)建和銷毀的監(jiān)聽器,ServletContext是整個(gè)web應(yīng)用的全局對象,它和Web應(yīng)用的生命周期綁定在一起,因此使用這個(gè)監(jiān)聽器對Web應(yīng)用的全局信息進(jìn)行初始化和銷毀操作,例如spring容器的初始化操作。比較有意思的是過濾器,在struts2里有個(gè)攔截器,它們的作用相同都是用來攔截請求的,因?yàn)閿r截器是struts2的特有功能,在struts2里使用攔截器自然比使用過濾器更順手,其實(shí)攔截器所用的技術(shù)比過濾器更加先進(jìn),因?yàn)閿r截器使用了反射技術(shù),因此攔截器攔截的面更大,控制請求的能力更強(qiáng),它能完成的任務(wù)也會更加的豐富多彩。
在我第一次接觸struts2時(shí)候,有人告訴我struts設(shè)計(jì)的一個(gè)目的就是想屏蔽在控制層里操作request和response對象,因?yàn)檫@兩個(gè)http協(xié)議的兒子會造成web開發(fā)里思路的混亂,但是我在實(shí)際開發(fā)里卻經(jīng)常不自覺的使用這兩個(gè)對象。而且本人做前端開發(fā)非常喜歡使用ajax,使用ajax技術(shù)時(shí)候我就很討厭struts2的自定義標(biāo)簽,我更加喜歡在頁面里用javascript技術(shù)處理各種信息,最終struts2在我眼里就是一個(gè)servlet的變體,因此曾經(jīng)有段時(shí)間我常常在想是不是可以拋棄struts2,直接用servlet,因?yàn)閟truts2里用到了太多反射機(jī)制,特別是使用注解做配置(注解是用反射實(shí)現(xiàn)的),在java里反射的執(zhí)行效率是非常低的,直接使用servlet一定能提升web應(yīng)用的執(zhí)行效率。其實(shí)這個(gè)倒很難做到,因?yàn)楫?dāng)時(shí)我沒法在servlet里靈活的運(yùn)用spring技術(shù)。
^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^?說完Servlet+jsp技術(shù)到Struts2技術(shù)的過渡。接下來談?wù)凷pring。
spring技術(shù)可以說是java企業(yè)開發(fā)里最重要的技術(shù),不過真的理解spring的作用和意義還真是一件麻煩的事情,很多人對spring理解其實(shí)都是停留在使用階段(例如:聲明式事務(wù)很好用等等),當(dāng)今的spring技術(shù)生態(tài)環(huán)境里可謂是蔚為壯觀,spring已經(jīng)包羅萬象,它的內(nèi)容之多完全不亞于它的本源java語言了,而spring這么大的框都是建立在ioc和aop技術(shù)之上,只有深入理解了這兩個(gè)技術(shù)我們才能明白為什么spring這個(gè)框能裝的下那么多東西了。
首先是ioc,ioc技術(shù)第一個(gè)解釋叫做控制反轉(zhuǎn),它還有個(gè)解釋就是依賴注入,這兩個(gè)名字很難從字面理解,但是當(dāng)你理解它的原理后就會發(fā)現(xiàn)它們的描述是何等準(zhǔn)確。Ioc技術(shù)的本質(zhì)就是構(gòu)建對象的技術(shù)換句話說就是將一個(gè)類實(shí)例化成對象的技術(shù),在java里實(shí)例化類通過new關(guān)鍵字進(jìn)行的,每次new一個(gè)類都會產(chǎn)生一個(gè)新的實(shí)例對象,這么做視乎很浪費(fèi),有時(shí)這種浪費(fèi)還挺危險(xiǎn),因?yàn)樵诔绦蜷_發(fā)時(shí)候我們常常只需要某個(gè)類永遠(yuǎn)只能產(chǎn)生一個(gè)的實(shí)例對象這個(gè)時(shí)候就得使用單例模式,此外在設(shè)計(jì)模式里還可以通過工廠方式產(chǎn)生對象,使用過spring的人看到上面的文字就知道了,spring里bean的定義就和上面的內(nèi)容一一對應(yīng),scope屬性single產(chǎn)生單例對象,prototype產(chǎn)生新對象,bean還可以通過工廠方式產(chǎn)生對象,可以說spring的bean就是制造對象的工具。面向?qū)ο缶幊汤飳ο笙喈?dāng)于顯示生活中的一個(gè)實(shí)體,例如我們有個(gè)對象作用是完成打獵的操作,那么打獵這個(gè)對象內(nèi)部包含兩個(gè)輔助對象:人和槍,只有人和槍賦予了打獵這個(gè)對象,那么打獵對象才能完成打獵的操作,但是構(gòu)建一個(gè)人和槍的對象并不是看起來那么簡單,這里以槍為例,要?jiǎng)?chuàng)造一把槍我們需要金屬,需要機(jī)床,需要子彈,而機(jī)床和子彈又是兩個(gè)新對象,這些對象一個(gè)個(gè)相互嵌套相互關(guān)聯(lián),大伙試想下如果我們在java代碼里構(gòu)建一個(gè)槍的對象那是何其的復(fù)雜,假如我們要構(gòu)造的不是簡單的槍對象而是更加復(fù)雜的航空母艦,那么構(gòu)造這個(gè)對象的成本之高是讓人難以想象的,怎么來消除這種對象相互嵌套相互依賴的關(guān)系了?spring提供了一種方式,這種方式就是spring提供一個(gè)容器,我們在xml文件里定義各個(gè)對象的依賴關(guān)系,由容器完成對象的構(gòu)建,當(dāng)我們java代碼里需要使用某個(gè)實(shí)例的時(shí)候就可以從容器里獲取,那么對象的構(gòu)建操作就被spring容器接管,所以它被稱為控制反轉(zhuǎn),控制反轉(zhuǎn)的意思就是本來屬于java程序里構(gòu)建對象的功能交由容器接管,依賴注入就是當(dāng)程序要使用某個(gè)對象時(shí)候,容器會把它注入到程序里,這就叫做依賴注入。在java開發(fā)里我們想使用某個(gè)類提供的功能,有兩種方式,一種就是構(gòu)造一個(gè)新的類,新的類繼承該類,另一種方式則是將某個(gè)類定義在新類里,那么兩個(gè)類之間就建立一種關(guān)聯(lián)關(guān)系,spring的ioc容器就是實(shí)現(xiàn)了這種關(guān)聯(lián)關(guān)系(記住不是繼承關(guān)系哦),那么某個(gè)類要被賦予到新類有哪些辦法了?一般只有兩種:一種就是通過構(gòu)造函數(shù),一種就是通過setXXX方式,這也是spring容器使用到了兩種標(biāo)準(zhǔn)的注入方式。
不管是上面說的繼承方式,還是關(guān)聯(lián)方式其實(shí)都是增強(qiáng)目標(biāo)對象能力的開發(fā)手段,在設(shè)計(jì)模式里有一種代理模式,代理模式將繼承模式和關(guān)聯(lián)模式結(jié)合在一起使用,代理模式就是繼承模式和關(guān)聯(lián)模式的綜合體,不過這個(gè)綜合體的作用倒不是解決對象注入的問題,而是為具體操作對象找到一個(gè)保姆或者是秘書,這就和小說里的二號首長一樣,這個(gè)二號首長對外代表了具體的實(shí)例對象,實(shí)例對象的入口和出口都是通過這個(gè)二號首長,因?yàn)榫唧w的實(shí)例對象是一號首長,一號首長是要干大事的,所以一些事務(wù)性,重復(fù)性的工作例如泡茶,安排車子,這樣的工作是不用勞煩一號首長的大駕,而是二號首長幫忙解決的,這就是aop的思想,aop解決程序開發(fā)里事務(wù)性,和核心業(yè)務(wù)無關(guān)的問題,但這些問題對于業(yè)務(wù)場景的實(shí)現(xiàn)是很有必要的,在實(shí)際開發(fā)里aop也是節(jié)省代碼的一種方式。
Spring的核心技術(shù)的作用本質(zhì)就是一個(gè)溝通機(jī)制,spring總是盡全力的讓溝通的雙方信息暢通,同時(shí)降低雙方的溝通成本,在現(xiàn)實(shí)機(jī)構(gòu)里一個(gè)善于溝通的人肯定是該公司的領(lǐng)導(dǎo),很會溝通的領(lǐng)導(dǎo)能調(diào)動(dòng)起各種資源的積極性,善于溝通的領(lǐng)導(dǎo)就會做到海納百川,讓各種不同人追隨他,所以當(dāng)今的spring就是一個(gè)大框,什么都可以往里裝。Spring很像銀行,它不能直接創(chuàng)造物質(zhì)財(cái)富,但是一切資源都要通過它進(jìn)行流通,它能控制經(jīng)濟(jì)發(fā)展的走向,回到程序的世界,spring的作用是被標(biāo)榜為程序之間的解耦,spring能降低不同模塊之間的耦合度,原因就是在程序開發(fā)里不同模塊之間信息的溝通是通過對象傳遞完成的,而對象能否順利傳遞就是要合理的構(gòu)建好對象,而管理好對象的構(gòu)建方式就能管理好對象傳遞,這就是spring給系統(tǒng)架構(gòu)設(shè)計(jì)帶來的好處。
^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^ ^_^?說到Spring, Spring的事務(wù)你懂嗎?
什么是事務(wù)?為什么事務(wù)要管理?什么是Spring事務(wù)?事務(wù)就是對一系列的數(shù)據(jù)庫操作(比如插入多條數(shù)據(jù))進(jìn)行統(tǒng)一的提交或回滾操作,如果插入成功,那么一起成功,如果中間有一條出現(xiàn)異常,那么回滾之前的所有操作。這樣可以防止出現(xiàn)臟數(shù)據(jù),防止數(shù)據(jù)庫數(shù)據(jù)出現(xiàn)問題。開發(fā)中為了避免這種情況一般都會進(jìn)行事務(wù)管理。在JDBC中,是通過Connection對象進(jìn)行事務(wù)管理的,默認(rèn)是自動(dòng)提交事務(wù),可以手工將自動(dòng)提交關(guān)閉,通過commit方法進(jìn)行提交,rollback方法進(jìn)行回滾,如果不提交,則數(shù)據(jù)不會真正的插入到數(shù)據(jù)庫中。Hibernate中則是通過Transaction進(jìn)行事務(wù)管理,處理方法與JDBC中類似。Spring中也有自己的事務(wù)管理機(jī)制,一般是使用TransactionMananger進(jìn)行管理,可以通過Spring的注入完成此功能。
我通俗的理解如下:spring只是控制數(shù)據(jù)庫的事務(wù)提交和回滾,借助于java的反射機(jī)制,在事務(wù)控制的方法(通常是service層的方法)前后獲取事務(wù)開啟session,然后執(zhí)行你的數(shù)據(jù)操作,如果你的方法內(nèi)有異常被拋出,spring會捕獲異常并回滾你在這個(gè)方法內(nèi)所有的數(shù)據(jù)操作,如果成功則提交所有的數(shù)據(jù),最后spring會幫你關(guān)閉需要關(guān)閉的東西。所以spring想要做的是,要程序員專注于寫邏輯,不需要關(guān)心數(shù)據(jù)庫何時(shí)開啟和關(guān)閉連接。再說的通俗點(diǎn)兒:事務(wù),對于一件事,對了就提交,錯(cuò)了就回滾,什么時(shí)候回滾,都是事務(wù)要做的事情。具體的操作由spring 配置來管理(同時(shí)你也可以脫離框架,自己寫事務(wù)管理方法)。
使用Spring事務(wù)的優(yōu)點(diǎn)?
?
在SSH框假中Spring充當(dāng)了管理容器的角色。我們都知道Hibernate用來做持久層,因?yàn)樗鼘DBC做了一個(gè)良好的封裝,程序員在與數(shù)據(jù)庫進(jìn)行交互時(shí)可以不用書寫大量的SQL語句。Struts是用來做應(yīng)用層的,他它負(fù)責(zé)調(diào)用業(yè)務(wù)邏輯serivce層。所以SSH框架的流程大致是:Jsp頁面----Struts------Service(業(yè)務(wù)邏輯處理類)---Hibernate(左到右)。struts負(fù)責(zé)控制Service(業(yè)務(wù)邏輯處理類),從而控制了Service的生命周期,這樣層與層之間的依賴很強(qiáng),屬于耦合。這時(shí),使用spring框架就起到了控制Action對象(Strus中的)和Service類的作用,兩者之間的關(guān)系就松散了,Spring的Ioc機(jī)制(控制反轉(zhuǎn)和依賴注入)正是用在此處。Spring的Ioc(控制反轉(zhuǎn)和依賴注入) 控制反轉(zhuǎn):就是由容器控制程序之間的(依賴)關(guān)系,而非傳統(tǒng)實(shí)現(xiàn)中,由程序代碼直接操控 依賴注入:組件之間的依賴關(guān)系由容器在運(yùn)行期決定 ,由容器動(dòng)態(tài)的將某種依賴關(guān)系注入到組件之中。 從上面我們不難看出:從頭到尾Action僅僅是充當(dāng)了Service的控制工具,這些具體的業(yè)務(wù)方法是怎樣實(shí)現(xiàn)的,他根本就不會管,也不會問,他只要知道這些業(yè)務(wù)實(shí)現(xiàn)類所提供的方法接口就可以了。而在以往單獨(dú)使用Struts框架的時(shí)候,所有的業(yè)務(wù)方法類的生命周期,甚至是一些業(yè)務(wù)流程都是由Action來控制的。層與層之間耦合性太緊密了,既降低了數(shù)據(jù)訪問的效率又使業(yè)務(wù)邏輯看起來很復(fù)雜,代碼量也很多。Spring容器控制所有Action對象和業(yè)務(wù)邏輯類的生命周期,由于上層不再控制下層的生命周期,層與層之間實(shí)現(xiàn)了完全脫耦,使程序運(yùn)行起來效率更高,維護(hù)起來也方便。使用Spring的第二個(gè)好處(AOP應(yīng)用): 事務(wù)的處理: 在以往的JDBCTemplate中事務(wù)提交成功,異常處理都是通過Try/Catch 來完成,而在Spring中。Spring容器集成了TransactionTemplate,她封裝了所有對事務(wù)處理的功能,包括異常時(shí)事務(wù)回滾,操作成功時(shí)數(shù)據(jù)提交等復(fù)雜業(yè)務(wù)功能。這都是由Spring容器來管理,大大減少了程序員的代碼量,也對事務(wù)有了很好的管理控制。Hibernate中也有對事務(wù)的管理,hibernate中事務(wù)管理是通過SessionFactory創(chuàng)建和維護(hù)Session來完成。而Spring對SessionFactory配置也進(jìn)行了整合,不需要在通過hibernate.cfg.xml來對SessionaFactory進(jìn)行設(shè)定。這樣的話就可以很好的利用Sping對事務(wù)管理強(qiáng)大功能。避免了每次對數(shù)據(jù)操作都要現(xiàn)獲得Session實(shí)例來啟動(dòng)事務(wù)/提交/回滾事務(wù)還有繁瑣的Try/Catch操作。這些也就是Spring中的AOP(面向切面編程)機(jī)制很好的應(yīng)用。一方面使開發(fā)業(yè)務(wù)邏輯更清晰、專業(yè)分工更加容易進(jìn)行。另一方面就是應(yīng)用Spirng AOP隔離降低了程序的耦合性使我們可以在不同的應(yīng)用中將各個(gè)切面結(jié)合起來使用大大提高了代碼重用度。有利于代碼重用,特別是Dao代碼的重用。事務(wù)往往和業(yè)務(wù)規(guī)則緊密關(guān)聯(lián)。當(dāng)業(yè)務(wù)邏輯發(fā)生改變,意味著dao的大幅度改動(dòng)。系統(tǒng)規(guī)模達(dá)到一定程度,修改風(fēng)險(xiǎn)相當(dāng)大。Spring的好處是不更改現(xiàn)有的dao,僅需對現(xiàn)有的service bean進(jìn)行配置就達(dá)到事務(wù)效果了。同時(shí),把事務(wù)統(tǒng)一在service層,系統(tǒng)結(jié)構(gòu)更清晰。 為什么說風(fēng)險(xiǎn)風(fēng)大? Spring對于事務(wù)的配置有兩種方式:第一種,使用xml形式,第二種,使用注解的形式。 基于XMl方式: 優(yōu)點(diǎn):可以在后期維護(hù)的時(shí)候適當(dāng)?shù)恼{(diào)整事務(wù)管理模式,并且只要遵循一定的命名規(guī)范,可以讓程序員不必關(guān)心事務(wù)。缺點(diǎn):系統(tǒng)越龐大,xml配置就越大。 基于注解方式:優(yōu)點(diǎn):配置比較方便,程序員只要在service層代碼設(shè)置即可以實(shí)現(xiàn)。不需要知道系統(tǒng)需要多少個(gè)bean,交給容器來注入就好了。缺點(diǎn):當(dāng)你要修改或刪除一個(gè)bean的時(shí)候,你無法確定到底有多少個(gè)其他的bean依賴于這個(gè)bean。(解決方法:需要有嚴(yán)格的開發(fā)文檔,在修改實(shí)現(xiàn)時(shí)盡可能繼續(xù)遵守相應(yīng)的接口避免使其他依賴于此的bean不可用)在我們用SSH開發(fā)項(xiàng)目的時(shí)候,我們一般都是將事務(wù)設(shè)置在Service層 那么當(dāng)我們調(diào)用Service層的一個(gè)方法的時(shí)候它能夠保證我們的這個(gè)方法中執(zhí)行的所有的對數(shù)據(jù)庫的更新操作保持在一個(gè)事務(wù)中,在事務(wù)層里面調(diào)用的這些方法要么全部成功,要么全部失敗。那么事務(wù)的傳播特性也是從這里說起的。?如果你在你的Service層的這個(gè)方法中,除了調(diào)用了Dao層的方法之外,還調(diào)用了本類的其他的Service方法,那么在調(diào)用其他的 Service方法的時(shí)候,這個(gè)事務(wù)是怎么規(guī)定的呢,我必須保證我在我方法里掉用的這個(gè)方法與我本身的方法處在同一個(gè)事務(wù)中,否則如何保證事物的一致性。事務(wù)的傳播特性就是解決這個(gè)問題的,“事務(wù)是會傳播的”在Spring中有針對傳播特性的多種配置我們大多數(shù)情況下只用其中的一種:PROPGATION_REQUIRED:這個(gè)配置項(xiàng)的意思是說當(dāng)我調(diào)用service層的方法的時(shí)候開啟一個(gè)事務(wù)(具體調(diào)用那一層的方法開始創(chuàng)建事務(wù),要看你的aop的配置),那么在調(diào)用這個(gè)service層里面的其他的方法的時(shí)候,如果當(dāng)前方法產(chǎn)生了事務(wù)就用當(dāng)前方法產(chǎn)生的事務(wù),否則就創(chuàng)建一個(gè)新的事務(wù)。這個(gè)工作是由Spring來幫助我們完成的。?以前沒有Spring幫助我們完成事務(wù)的時(shí)候我們必須自己手動(dòng)的控制事務(wù),例如當(dāng)我們項(xiàng)目中僅僅使用hibernate,而沒有集成進(jìn) spring的時(shí)候,我們在一個(gè)service層中調(diào)用其他的業(yè)務(wù)邏輯方法,為了保證事物必須也要把當(dāng)前的hibernate session傳遞到下一個(gè)方法中,或者采用ThreadLocal的方法,將session傳遞給下一個(gè)方法,其實(shí)都是一個(gè)目的。現(xiàn)在這個(gè)工作由 spring來幫助我們完成,就可以讓我們更加的專注于我們的業(yè)務(wù)邏輯。而不用去關(guān)心事務(wù)的問題。默認(rèn)情況下當(dāng)發(fā)生RuntimeException的情況下,事務(wù)才會回滾,所以要注意一下。如果你在程序發(fā)生錯(cuò)誤的情況下,有自己的異常處理機(jī)制定義自己的Exception,必須從RuntimeException類繼承,這樣事務(wù)才會回滾!
補(bǔ)充文字來說一下上面的關(guān)于Spring的一個(gè)問題。基于xml和基于注解,當(dāng)然了它們都有優(yōu)缺點(diǎn)。我們通俗的說,是這樣的。先來回顧一下傳統(tǒng)上是如何配置 Bean 并完成 Bean 之間依賴關(guān)系的建立。下面是 3 個(gè)類,它們分別是 Office、Car 和 Boss,這 3 個(gè)類需要在 Spring 容器中配置為 Bean。
// Office.java public class Office {private String officeNo =”001”;//省略 get/setter@Overridepublic String toString() {return "officeNo:" + officeNo;} }?
// Car.java public class Car {private String brand;private double price;// 省略 get/setter@Overridepublic String toString() {return "brand:" + brand + "," + "price:" + price;} }?
// Boss.java public class Boss {private Car car;private Office office;// 省略 get/setter@Overridepublic String toString() {return "car:" + car + "\n" + "office:" + office;} }?
我們在 Spring 容器中將 Office 和 Car 聲明為 Bean,并注入到 Boss Bean 中:下面是使用傳統(tǒng) XML?完成這個(gè)工作的配置文件 beans.xml:
// bean.xml將以上三個(gè)類配置成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>?
// 當(dāng)我們運(yùn)行這段代碼時(shí),控制臺將正確打印出boss的信息。這說明 Spring 容器已經(jīng)正確完成了 Bean 創(chuàng)建和裝配的工作。 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?注釋,它可以對類成員變量、方法及構(gòu)造函數(shù)進(jìn)行標(biāo)注,完成自動(dòng)裝配的工作。來看一下使用@Autowired?進(jìn)行成員變量自動(dòng)注入的代碼:?
// Autowired是自動(dòng)裝配的意思 import org.springframework.beans.factory.annotation.Autowired;public class Boss {@Autowiredprivate Car car;@Autowiredprivate Office office; }// Spring 通過一個(gè) BeanPostProcessor 對 @Autowired 進(jìn)行解析,所以要讓@Autowired 起作用必須事先在 Spring 容器中聲明 AutowiredAnnotationBeanPostProcessor Bean。 // 讓系統(tǒng)認(rèn)識@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 將自動(dòng)起作用,對標(biāo)注 @Autowired 的 Bean 進(jìn)行自動(dòng)注入 --><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> ?這樣,當(dāng) Spring 容器啟動(dòng)時(shí),AutowiredAnnotationBeanPostProcessor?將掃描 Spring 容器中所有 Bean,當(dāng)發(fā)現(xiàn) Bean 中擁有@Autowired?注釋時(shí)就找到和其匹配(默認(rèn)按類型匹配)的 Bean,并注入到對應(yīng)的地方中去。按照上面的配置,Spring 將直接采用 Java 反射機(jī)制對 Boss 中的
car?和?office?這兩個(gè)私有成員變量進(jìn)行自動(dòng)注入。所以對成員變量使用@Autowired?后,您大可將它們的 setter 方法(setCar()?和?setOffice())從 Boss 中刪除。當(dāng)然,您也可以通過?@Autowired?對方法或構(gòu)造函數(shù)進(jìn)行標(biāo)注,來看下面的代碼:
public class Boss {private Car car;private Office office;@Autowiredpublic void setCar(Car car) {this.car = car;}@Autowiredpublic void setOffice(Office office) {this.office = office;} }?
這時(shí),@Autowired 將查找被標(biāo)注的方法的入?yún)㈩愋偷?Bean,并調(diào)用方法自動(dòng)注入這些 Bean。而下面的使用方法則對構(gòu)造函數(shù)進(jìn)行標(biāo)注:Spring?IOC三種注入方式(接口注入、setter注入、構(gòu)造器注入)
public class Boss {private Car car;private Office office;@Autowiredpublic Boss(Car car ,Office office){this.car = car;this.office = office ;} } // 由于 Boss() 構(gòu)造函數(shù)有兩個(gè)入?yún)?#xff0c;分別是 car 和 office,@Autowired 將分別尋找和它們類型匹配的 Bean,將它們作為Boss(Car car ,Office office) 的入?yún)韯?chuàng)建 Boss Bean。在默認(rèn)情況下使用 @Autowired 注釋進(jìn)行自動(dòng)注入時(shí),Spring 容器中匹配的候選 Bean 數(shù)目必須有且僅有一個(gè)。當(dāng)找不到一個(gè)匹配的 Bean 時(shí),Spring 容器將拋出BeanCreationException 異常,并指出必須至少擁有一個(gè)匹配的 Bean。我們可以來做一個(gè)實(shí)驗(yàn): <?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"> //然后你想,當(dāng)多個(gè)bean之間互相依賴的時(shí)候,是不是維護(hù)起來很麻煩啊。<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?屬性標(biāo)注了?@Autowired,當(dāng)啟動(dòng) Spring 容器時(shí),異常就產(chǎn)生了。當(dāng)不能確定 Spring 容器中一定擁有某個(gè)類的 Bean 時(shí),可以在需要自動(dòng)注入該類 Bean 的地方可以使用?@Autowired(required = false),這等于告訴 Spring:在找不到匹配 Bean 時(shí)也不報(bào)錯(cuò)。來看一下具體的例子:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Required;public class Boss {private Car car;private Office office;@Autowiredpublic void setCar(Car car) {this.car = car;}@Autowired(required = false)public void setOffice(Office office) {this.office = office;} }當(dāng)然,一般情況下,使用?@Autowired?的地方都是需要注入 Bean 的,使用了自動(dòng)注入而又允許不注入的情況一般僅會在開發(fā)期或測試期碰到(如為了快速啟動(dòng) Spring 容器,僅引入一些模塊的 Spring 配置文件),所以@Autowired(required = false)?會很少用到。和找不到一個(gè)類型匹配 Bean 相反的一個(gè)錯(cuò)誤是:如果 Spring 容器中擁有多個(gè)候選 Bean,Spring 容器在啟動(dòng)時(shí)也會拋出?BeanCreationException?異常。來看下面的例子:
// 在 beans.xml 中配置兩個(gè) 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 容器中配置了兩個(gè)類型為?Office?類型的 Bean,當(dāng)對 Boss 的?office?成員變量進(jìn)行自動(dòng)注入時(shí),Spring 容器將無法確定到底要用哪一個(gè) Bean,因此異常發(fā)生了。Spring 允許我們通過?@Qualifier注釋指定注入 Bean 的名稱,這樣歧義就消除了,可以通過下面的方法解決異常:
@Autowired public void setOffice(@Qualifier("office")Office office) {this.office = office; }@Qualifier("office")?中的?office?是 Bean 的名稱,所以?@Autowired?和@Qualifier?結(jié)合使用時(shí),自動(dòng)注入的策略就從 byType 轉(zhuǎn)變成 byName 了。@Autowired?可以對成員變量、方法以及構(gòu)造函數(shù)進(jìn)行注釋,而@Qualifier?的標(biāo)注對象是成員變量、方法入?yún)ⅰ?gòu)造函數(shù)入?yún)ⅰU怯捎谧⑨寣ο蟮牟煌?#xff0c;所以 Spring 不將?@Autowired?和@Qualifier?統(tǒng)一成一個(gè)注釋類。下面是對成員變量和構(gòu)造函數(shù)入?yún)⑦M(jìn)行注釋的代碼:對成員變量進(jìn)行注釋:
public class Boss {@Autowiredprivate Car car;@Autowired@Qualifier("office")private Office office; }?我們面試中提到Spring框架經(jīng)常被問到的一塊兒是Spring的事務(wù),它優(yōu)于struts2,優(yōu)于hibernate。那么我們現(xiàn)在就詳細(xì)的說說這個(gè)東西。
// 事務(wù)的特性:原子性、一致性、隔離性、持久性。常常問的:1. Spring事務(wù)的傳播特性; 2. Spring事務(wù)的隔離機(jī)制。?我們先來探討一下數(shù)據(jù)庫事務(wù)的隔離級別:
事務(wù)的(ACID)特性是由關(guān)系數(shù)據(jù)庫管理系統(tǒng)(RDBMS,數(shù)據(jù)庫系統(tǒng))來實(shí)現(xiàn)的。數(shù)據(jù)庫管理系統(tǒng)采用日志來保證事務(wù)的原子性、一致性和持久性。日志記錄了事務(wù)對數(shù)據(jù)庫所做的更新,如果某個(gè)事務(wù)在執(zhí)行過程中發(fā)生錯(cuò)誤,就可以根據(jù)日志,撤銷事務(wù)對數(shù)據(jù)庫已做的更新,使數(shù)據(jù)庫退回到執(zhí)行事務(wù)前的初始狀態(tài)。數(shù)據(jù)庫管理系統(tǒng)采用鎖機(jī)制來實(shí)現(xiàn)事務(wù)的隔離性。當(dāng)多個(gè)事務(wù)同時(shí)更新數(shù)據(jù)庫中相同的數(shù)據(jù)時(shí),只允許持有鎖的事務(wù)能更新該數(shù)據(jù),其他事務(wù)必須等待,直到前一個(gè)事務(wù)釋放了鎖,其他事務(wù)才有機(jī)會更新該數(shù)據(jù)。幻讀 是指當(dāng)事務(wù)不是獨(dú)立執(zhí)行時(shí)發(fā)生的一種現(xiàn)象,例如第一個(gè)事務(wù)對一個(gè)表中的數(shù)據(jù)進(jìn)行了修改,比如這種修改涉及到表中的“全部數(shù)據(jù)行”。同時(shí),第二個(gè)事務(wù)也修改這個(gè)表中的數(shù)據(jù),這種修改是向表中插入“一行新數(shù)據(jù)”。那么,以后就會發(fā)生操作第一個(gè)事務(wù)的用戶發(fā)現(xiàn)表中還有沒有修改的數(shù)據(jù)行,就好象發(fā)生了幻覺一樣.一般解決幻讀的方法是增加范圍鎖RangeS,鎖定檢鎖范圍為只讀,這樣就避免了幻讀。臟讀 就是指當(dāng)一個(gè)事務(wù)正在訪問數(shù)據(jù),并且對數(shù)據(jù)進(jìn)行了修改,而這種修改還沒有提交到數(shù)據(jù)庫中,這時(shí),另外一個(gè)事務(wù)也訪問這個(gè)數(shù)據(jù),然后使用了這個(gè)數(shù)據(jù)。因?yàn)檫@個(gè)數(shù)據(jù)是還沒有提交的數(shù)據(jù),那么另外一個(gè)事務(wù)讀到的這個(gè)數(shù)據(jù)是臟數(shù)據(jù),依據(jù)臟數(shù)據(jù)所做的操作可能是不正確的。數(shù)據(jù)庫系統(tǒng)有四個(gè)隔離級別。對數(shù)據(jù)庫使用何種隔離級別要審慎分析,因?yàn)榫S護(hù)一個(gè)最高的隔離級別雖然會防止數(shù)據(jù)的出錯(cuò),但是卻導(dǎo)致了并行度的損失,以及導(dǎo)致死鎖出現(xiàn)的可能性增加。然而,降低隔離級別,卻會引起一些難以發(fā)現(xiàn)的bug。
1. 序列化 Serializable,?添加范圍鎖(比如表鎖,頁鎖等,關(guān)于range lock,我也沒有很深入的研究),直到transaction A結(jié)束。以此阻止其它trasaction B對此范圍內(nèi)的insert,update等操作。幻讀,臟讀,不可重復(fù)讀等問題都不會發(fā)生。
?
2. 可重復(fù)讀 repeatable read,?對于讀出的記錄,添加共享鎖直到transaction A結(jié)束。其它transaction B對這個(gè)記錄的試圖修改會一直等待直到trasaction A結(jié)束。InnoDB?默認(rèn)級別。可能發(fā)生的問題:當(dāng)執(zhí)行一個(gè)范圍查詢時(shí),可能會發(fā)生幻讀。
?
3. 提交讀 read commited,?在trasaction A中讀取數(shù)據(jù)時(shí)對記錄添加共享鎖,但讀取結(jié)束立即釋放。其它transaction B對這個(gè)記錄的試圖修改會一直等待直到A中的讀取過程結(jié)束,而不需要整個(gè)trasaction A的結(jié)束。所以,在trasaction A的不同階段對同一記錄的讀取結(jié)果可能是不同的。
? ? ? ?可能發(fā)生的問題:不可重復(fù)讀。
?
4. 未提交讀 read uncommited,?不添加共享鎖。所以其它trasaction B可以在trasaction A對記錄的讀取過程中修改同一記錄,可能會導(dǎo)致A讀取的數(shù)據(jù)是一個(gè)被破壞的或者說不完整不正確的數(shù)據(jù)。另外,在trasaction A中可以讀取到trasaction B(未提交)中修改的數(shù)據(jù)。比如trasaction B對R記錄修改了,但未提交。此時(shí),在Trasaction A中讀取R記錄,讀出的是被B修改過的數(shù)據(jù)。可能發(fā)生的問題:臟讀。
?那么Spring的事務(wù)隔離機(jī)制和Spring事務(wù)的傳播屬性呢,Spring的隔離級別我們知道目的是為了防止幻讀和臟讀。Spring的事務(wù)傳播屬性似乎好難理解。。接下來,先解釋下什么叫做事務(wù)的傳播屬性:
我們都知道事務(wù)的概念,那么事務(wù)的傳播特性是什么呢?(先著重介紹傳播特性的概念,關(guān)于傳播特性的相關(guān)配置稍后再介紹) 背景:當(dāng)我們用SSH開發(fā)項(xiàng)目的時(shí)候,我們一般都是將事務(wù)設(shè)置在Service層 那么當(dāng)我們調(diào)用Service層的一個(gè)方法的時(shí)候它能夠保證我們的這個(gè)方法中執(zhí)行的所有的對數(shù)據(jù)庫的更新操作保持在一個(gè)事務(wù)中,在事務(wù)層里面調(diào)用的這些方法要么全部成功,要么全部失敗。那么事務(wù)的傳播特性也是從這里說起的。 場景:如果你在你的Service層的這個(gè)方法中,除了調(diào)用了Dao層的方法之外,還調(diào)用了本類的其他的Service方法,那么在調(diào)用其他的Service方法的時(shí)候,這個(gè)事務(wù)是怎么規(guī)定的呢,我必須保證我在我方法里掉用的這個(gè)方法與我本身的方法處在同一個(gè)事務(wù)中,否則如何保證事物的一致性。事務(wù)的傳播特性就是解決這個(gè)問題的,“事務(wù)是會傳播的”在Spring中有針對傳播特性的多種配置我們大多數(shù)情況下只用其中的1種:PROPGATION_REQUIRED:這個(gè)配置項(xiàng)的意思是說當(dāng)我調(diào)用service層的方法的時(shí)候開啟一個(gè)事務(wù)(具體調(diào)用那一層的方法開始創(chuàng)建事務(wù),要看你的aop的配置),那么在調(diào)用這個(gè)service層里面的其他的方法的時(shí)候,如果當(dāng)前方法產(chǎn)生了事務(wù)就用當(dāng)前方法產(chǎn)生的事務(wù),否則就創(chuàng)建一個(gè)新的事務(wù)。這個(gè)工作使由Spring來幫助我們完成的。 以前沒有Spring幫助我們完成事務(wù)的時(shí)候我們必須自己手動(dòng)的控制事務(wù),例如當(dāng)我們項(xiàng)目中僅僅使用hibernate,而沒有集成進(jìn)spring的時(shí)候,我們在一個(gè)service層中調(diào)用其他的業(yè)務(wù)邏輯方法,為了保證事物必須也要把當(dāng)前的hibernate session傳遞到下一個(gè)方法中,或者采用ThreadLocal的方法,將session傳遞給下一個(gè)方法,其實(shí)都是一個(gè)目的。現(xiàn)在這個(gè)工作由spring來幫助我們完成,就可以讓我們更加的專注于我們的業(yè)務(wù)邏輯。而不用去關(guān)心事務(wù)的問題。默認(rèn)情況下當(dāng)發(fā)生RuntimeException的情況下,事務(wù)才會回滾,所以要注意一下如果你在程序發(fā)生錯(cuò)誤的情況下,有自己的異常處理機(jī)制定義自己的Exception,必須從RuntimeException類繼承這樣事務(wù)才會回滾!好了,知道了事務(wù)的傳播特性之后,我們看看Spring提供的六種事務(wù)傳播Propagation特性:仔細(xì)感覺一下,六中Spring事務(wù)傳播機(jī)制?對稱美?
1. Propagation required, 如果當(dāng)前沒有事務(wù),就新建一個(gè)事務(wù)。 2. Propagation supports, 如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。 3. Propagation mandatory, 如果當(dāng)前沒有事務(wù),就拋出異常。 4. Propagation requires new, 如果當(dāng)前存在事務(wù),掛起當(dāng)前事務(wù),新建事務(wù)。 5. Propagation not supported, 如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起,以非事務(wù)方式執(zhí)行。 6. Propagation never, 如果當(dāng)前存在事務(wù),拋出異常。以非事務(wù)方式執(zhí)行。關(guān)于六種事務(wù)傳播機(jī)制的具體應(yīng)用場景,參考:?Spring事務(wù)傳播機(jī)制博客
?了解了Spring事務(wù)的傳播特性,再來看看Spring事務(wù)的五種隔離級別isolation level。
// 先再來回顧前面說到的三個(gè)概念 臟讀: 指當(dāng)一個(gè)事務(wù)正在訪問數(shù)據(jù),并且對數(shù)據(jù)進(jìn)行了修改,而這種修改還沒有提交到數(shù)據(jù)庫中,這時(shí),另外一個(gè)事務(wù)也訪問這個(gè)數(shù)據(jù),然后使用了這個(gè)數(shù)據(jù)。因?yàn)檫@個(gè)數(shù)據(jù)是還沒有提交的數(shù)據(jù), 那么另外一個(gè)事務(wù)讀到的這個(gè)數(shù)據(jù)是臟數(shù)據(jù),依據(jù)臟數(shù)據(jù)所做的操作可能是不正確的。不可重復(fù)讀: 指在一個(gè)事務(wù)內(nèi),多次讀同一數(shù)據(jù)。在這個(gè)事務(wù)還沒有結(jié)束時(shí),另外一個(gè)事務(wù)也訪問該同一數(shù)據(jù)。那么,在第一個(gè)事務(wù)中的兩次讀數(shù)據(jù)之間,由于第二個(gè)事務(wù)的修改,那么第一個(gè)事務(wù)兩次讀到的數(shù)據(jù)可能是不一樣的。這樣就發(fā)生了在一個(gè)事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是不一樣的,因此稱為是不可重復(fù)讀。幻覺讀: 指當(dāng)事務(wù)不是獨(dú)立執(zhí)行時(shí)發(fā)生的一種現(xiàn)象,例如第一個(gè)事務(wù)對一個(gè)表中的數(shù)據(jù)進(jìn)行了修改,這種修改涉及到表中的全部數(shù)據(jù)行。同時(shí),第二個(gè)事務(wù)也修改這個(gè)表中的數(shù)據(jù),這種修改是向表中插入一行新數(shù)據(jù)。那么,以后就會發(fā)生操作第一個(gè)事務(wù)的用戶發(fā)現(xiàn)表中還有沒有修改的數(shù)據(jù)行,就好象發(fā)生了幻覺一樣。?
// 再來看Spring事務(wù)的隔離級別isolation level:1. isolation default: 這是一個(gè)默認(rèn)的隔離級別,使用數(shù)據(jù)庫默認(rèn)的事務(wù)隔離級別.(一般情況下,使用這種)。另外四個(gè)與JDBC的隔離級別相對應(yīng).2. isolation read uncommited: 這個(gè)是事務(wù)最低的隔離級別,它允許令外一個(gè)事務(wù)可以看到這個(gè)事務(wù)未提交的數(shù)據(jù)。這種隔離級別會產(chǎn)生臟讀,不可重復(fù)讀和幻像讀。(trasaction B可以在trasaction A對記錄的讀取過程中修改同一記錄)產(chǎn)生臟讀、產(chǎn)生不重復(fù)讀、產(chǎn)生幻讀。3. isolation read commited: 保證一個(gè)事務(wù)修改的數(shù)據(jù)提交后才能被另外一個(gè)事務(wù)讀取。另外一個(gè)事務(wù)不能讀取該事務(wù)未提交的數(shù)據(jù).(trasaction B提交后trasaction A才能讀取)避免臟讀、會產(chǎn)生不重復(fù)讀、會產(chǎn)生幻讀。4. isolation repeatable read: 這種事務(wù)隔離級別可以防止臟讀,不可重復(fù)讀。但是可能出現(xiàn)幻讀。它除了保證一個(gè)事務(wù)不能讀取另一個(gè)事務(wù)未提交的數(shù)據(jù)外,還保證了避免下面的情況產(chǎn)生(不可重復(fù)讀)。避免臟讀、避免不重復(fù)讀、會產(chǎn)生幻讀。5. isolation serializable.這是花費(fèi)最高代價(jià)但是最可靠的事務(wù)隔離級別。事務(wù)被處理為順序執(zhí)行。 除了防止臟讀,不可重復(fù)讀外,還避免了幻讀。避免臟讀、避免不重復(fù)讀、避免幻讀。換種口吻說,聲明式事務(wù)。聲明式事務(wù)是Spring提供的對程序事務(wù)管理的方式之一。Sping的聲明式事務(wù),就是在配置文件中采用配置的方式對事務(wù)進(jìn)行管理。Spring中的AOP即,是完成事務(wù)管理工作的。
總結(jié)一下我們出幾道面試題整合一下Spring3的知識。
1. Spring的工作原理:
1.客戶端請求提交到DispatcherServlet 2. 由DispatcherServlet控制器查詢一個(gè)或多個(gè)HandlerMapping,找到處理請求的Controller 3. DispatcherServlet將請求提交到Controller 4. Controller調(diào)用業(yè)務(wù)邏輯處理后,返回ModelAndView 5. DispatcherServlet查詢一個(gè)或多個(gè)ViewResoler視圖解析器,找到ModelAndView指定的視圖 6. 視圖負(fù)責(zé)將結(jié)果顯示到客戶端?2. 為什么要用Spring?
1、Spring能很好的與各大框架進(jìn)行集成 2、創(chuàng)建對象時(shí),如果我們不用spring。需要用工廠模式來創(chuàng)建,這個(gè)spring相當(dāng)于工廠模式已經(jīng)幫我們做了創(chuàng)建對象的功能(IOC、依賴注入)。 3、在用Hibernate的時(shí)候,如果不用spring每次都要寫事務(wù)的提交代碼,有了spring可以通過AOP幫助我們管理事務(wù)。 4、面向切面編程(AOP)在要記錄日志的時(shí)候添加一條記錄后需要在數(shù)據(jù)里同時(shí)添加一條添加成功了或失敗的記錄,那么就可以用Spring的Aop來處理,雖然不用Aop也能做但是不用Spring的Aop就會寫很多重復(fù)的代碼。AOP 讓開發(fā)人員可以創(chuàng)建非行為性的關(guān)注點(diǎn),稱為橫切關(guān)注點(diǎn),并將它們插入到應(yīng)用程序代碼中。使用 AOP 后,公共服務(wù) (比 如日志、持久性、事務(wù)等)就可以分解成方面并應(yīng)用到域?qū)ο笊?#xff0c;同時(shí)不會增加域?qū)ο蟮膶ο竽P偷膹?fù)雜性。IOC 允許創(chuàng)建一個(gè)可以構(gòu)造對象的應(yīng)用環(huán)境,然后向這些對象傳遞它們的協(xié)作對象。正如單詞 倒置 所表明的,IOC 就像反 過來的 JNDI。沒有使用一堆抽象工廠、服務(wù)定位器、單元素(singleton)和直接構(gòu)造(straight construction),每一個(gè)對象都是用 其協(xié)作對象構(gòu)造的。因此是由容器管理協(xié)作對象(collaborator)。Spring即使一個(gè)AOP框架,也是一IOC容器。 Spring 最好的地方是它有助于您替換對象。有了 Spring,只要用 JavaBean 屬性和配置文件加入依賴性(協(xié)作對象)。然后可以很容易地在需要時(shí)替換具有類似接口的協(xié)作對象。??3. 請你談?wù)凷SH的整合?
請你談?wù)凷SH整合 SSH:Struts(表示層)+Hibernate(持久層)+Spring(業(yè)務(wù)層)a、Struts Struts是一個(gè)表示層框架,主要作用是界面展示,接收請求,分發(fā)請求。 b、Hibernate Hibernate是一個(gè)持久層框架,它只負(fù)責(zé)與關(guān)系數(shù)據(jù)庫的操作。 c、Spring Spring是一個(gè)業(yè)務(wù)層框架,是一個(gè)整合的框架,能夠很好地黏合表示層與持久層。?
4. 介紹一下Spring的事務(wù)管理?
介紹一下Spring的事務(wù)管理 事務(wù)就是對一系列的數(shù)據(jù)庫操作(比如插入多條數(shù)據(jù))進(jìn)行統(tǒng)一的提交或回滾操作,如果插入成功,那么一起成功,如果中間有一條出現(xiàn)異常,那么回滾之前的所有操作。這樣可以防止出現(xiàn)臟數(shù)據(jù),防止數(shù)據(jù)庫數(shù)據(jù)出現(xiàn)問題。開發(fā)中為了避免這種情況一般都會進(jìn)行事務(wù)管理。Spring中也有自己的事務(wù)管理機(jī)制,一般是使用TransactionMananger進(jìn)行管理,可以通過Spring的注入來完成此功能。?
?5. 什么是依賴注入,依賴注入的作用是什么?
什么是依賴注入,依賴注入的作用是什么? IOC是一種思想,它能指導(dǎo)我們?nèi)绾卧O(shè)計(jì)出松耦合、更優(yōu)良的程序。傳統(tǒng)應(yīng)用程序都是由我們在類內(nèi)部主動(dòng)創(chuàng)建依賴對象,從而導(dǎo)致類與類之間高耦合,難于測試. 有了IOC容器后,把創(chuàng)建和查找依賴對象的控制權(quán)交給了容器,由容器進(jìn)行注入組合對象,所以對象與對象之間是松散耦合,這樣也方便測試,利于功能復(fù)用,更重要的是使得程序的整個(gè)體系結(jié)構(gòu)變得非常靈活。依賴注入的作用:減少類間耦合度,避免用new來創(chuàng)建對象。?6.什么是aop,aop的作用是什么?
什么是AOP,AOP的作用是什么? AOP,面向切面編程,就是把可重用的功能提取出來,然后將這些通用功能在合適的時(shí)候織入到應(yīng)用程序中,比如事務(wù)管理、權(quán)限控制、日志記錄、性能統(tǒng)計(jì)等。AOP的作用 AOP并沒有幫助我們解決任何新的問題,它只是提供了一種更好的辦法,能夠用更少的工作量來解決現(xiàn)有的一些問題,使得系統(tǒng)更加健壯,可維護(hù)性更好。7.?Spring中的BeanFactory與ApplicationContext的作用有哪些?
1、BeanFactory負(fù)責(zé)讀取bean的配置文件,管理bean的加載、實(shí)例化,維護(hù)bean之間的依賴關(guān)系,負(fù)責(zé)bean的生命周期。 2、ApplicationContext除了提供上述BeanFactory所能提供的功能之外,還提供了更完整的框架功能: a. 國際化支持 b. 資源訪問 c. 事件傳遞?
8. ?Hibernate的工作原理?
Hibernate工作原理及為什么要用? 原理: 1.讀取并解析配置文件 2.讀取并解析映射信息,創(chuàng)建SessionFactory 3.打開Sesssion 4.創(chuàng)建事務(wù)Transation 5.持久化操作 6.提交事務(wù) 7.關(guān)閉Session 8.關(guān)閉SesstionFactory?
9. 為什么要用hibernate?
// 為什么要用: 1. 對JDBC訪問數(shù)據(jù)庫的代碼做了封裝,大大簡化了數(shù)據(jù)訪問層繁瑣的重復(fù)性代碼。 2. Hibernate是一個(gè)基于JDBC的主流持久化框架,是一個(gè)優(yōu)秀的ORM實(shí)現(xiàn)。他很大程度的簡化DAO層的編碼工作 3. hibernate使用Java反射機(jī)制,而不是字節(jié)碼增強(qiáng)程序來實(shí)現(xiàn)透明性。 4. hibernate的性能非常好,因?yàn)樗莻€(gè)輕量級框架。映射的靈活性很出色。它支持各種關(guān)系數(shù)據(jù)庫,從一對一到多對多的各種復(fù)雜關(guān)系。?10. Hibernate如何延遲加載?
Hibernate是如何延遲加載? 1. Hibernate2延遲加載實(shí)現(xiàn):a)實(shí)體對象 b)集合(Collection) 2. Hibernate3 提供了屬性的延遲加載功能 當(dāng)Hibernate在查詢數(shù)據(jù)的時(shí)候,數(shù)據(jù)并沒有存在與內(nèi)存中,當(dāng)程序真正對數(shù)據(jù)的操作時(shí),對象才存在與內(nèi)存中,就實(shí)現(xiàn)了延遲加載,他節(jié)省了服務(wù)器的內(nèi)存開銷,從而提高了服務(wù)器的性能。11. Hibernate怎么樣實(shí)現(xiàn)類之間的關(guān)系?
Hibernate中怎樣實(shí)現(xiàn)類之間的關(guān)系?(如:一對多、多對多的關(guān)系)類與類之間的關(guān)系主要體現(xiàn)在表與表之間的關(guān)系進(jìn)行操作,它們都是對對象進(jìn)行操作,我們程序中把所有的表與類都映射在一起,它們通過配置文件中的many-to-one、one-to-many、many-to-many、?12. 說下hibernate的緩存機(jī)制。
詳細(xì)說下hibernate的緩存機(jī)制:// 為什么要用hibernate緩存:Hibernate是一個(gè)持久層框架,經(jīng)常訪問物理數(shù)據(jù)庫。為了降低應(yīng)用程序?qū)ξ锢頂?shù)據(jù)源訪問的頻次,從而提高應(yīng)用程序的運(yùn)行性能。緩存內(nèi)的數(shù)據(jù)是對物理數(shù)據(jù)源中的數(shù)據(jù)的復(fù)制,應(yīng)用程序在運(yùn)行時(shí)從緩存讀寫數(shù)據(jù),在特定的時(shí)刻或事件會同步緩存和物理數(shù)據(jù)源的數(shù)據(jù)。
?
?// Hibernate的緩存原理是怎么樣的?Hibernate一級緩存又稱為“Session的緩存”。Session的緩存是事務(wù)范圍的緩存(Session對象的生命周期通常對應(yīng)一個(gè)數(shù)據(jù)庫事務(wù)或者一個(gè)應(yīng)用事務(wù))。
Hibernate二級緩存又稱為“SessionFactory的緩存”。由于SessionFactory對象的生命周期和應(yīng)用程序的整個(gè)過程對應(yīng),因此Hibernate二級緩存是進(jìn)程范圍或者集群范圍的緩存,有可能出現(xiàn)并發(fā)問題,因此需要采用適當(dāng)?shù)牟l(fā)訪問策略,該策略為被緩存的數(shù)據(jù)提供了事務(wù)隔離級別。第二級緩存是可選的,是一個(gè)可配置的插件,默認(rèn)下SessionFactory不會啟用這個(gè)插件。
?
? ?// 既然二級緩存是進(jìn)程級別的緩存,那么它適合緩存什么類型的數(shù)據(jù)呢?
什么樣的數(shù)據(jù)適合存放到第二級緩存中?
1) 很少被修改的數(shù)據(jù)
2) 不是很重要的數(shù)據(jù),允許出現(xiàn)偶爾并發(fā)的數(shù)據(jù)
3) 不會被并發(fā)訪問的數(shù)據(jù)
4) 常量數(shù)據(jù)
不適合存放到第二級緩存的數(shù)據(jù)?
1) 經(jīng)常被修改的數(shù)據(jù)
2) 絕對不允許出現(xiàn)并發(fā)訪問的數(shù)據(jù),如財(cái)務(wù)數(shù)據(jù),絕對不允許出現(xiàn)并發(fā)
3) 與其他應(yīng)用共享的數(shù)據(jù)。
?
// Session的延遲加載
Session的延遲加載實(shí)現(xiàn)要解決兩個(gè)問題:正常關(guān)閉連接和確保請求中訪問的是同一個(gè)session。Hibernate session就是java.sql.Connection的一層高級封裝,一個(gè)session對應(yīng)了一個(gè)Connection。http請求結(jié)束后正確的關(guān)閉session(過濾器實(shí)現(xiàn)了session的正常關(guān)閉);延遲加載必須保證是同一個(gè)session(session綁定在ThreadLocal)。
?
// Hibernate查找對象, 如何應(yīng)用緩存?
當(dāng)Hibernate根據(jù)ID訪問數(shù)據(jù)對象的時(shí)候,首先從Session一級緩存中查;查不到,如果配置了二級緩存,那么從二級緩存中查;如果都查不到,再查詢數(shù)據(jù)庫,把結(jié)果按照ID放入到緩存刪除、更新、增加數(shù)據(jù)的時(shí)候,同時(shí)更新緩存。
?
??接下來說說Spring事務(wù)的配置:
Spring 如果沒有特殊說明,一般指是跟數(shù)據(jù)存儲有關(guān)的數(shù)據(jù)操作事務(wù)操作;對于數(shù)據(jù)持久操作的事務(wù)配置,一般有三個(gè)對象,數(shù)據(jù)源(dataSouce),事務(wù)管理器(transactionManager),以及事務(wù)代理機(jī)制;Spring 提供了多種的底層數(shù)據(jù)源實(shí)現(xiàn),以及多種類型的事務(wù)管理器;所有的管理器都基于 Platform Transaction Manager 接口實(shí)現(xiàn)各自的事務(wù)策略;Spring 事務(wù)管理采用 AOP 切面代理技術(shù)實(shí)現(xiàn),AOP 用于分隔關(guān)注點(diǎn),保證事務(wù)的原子性,采用一定的技術(shù) 把該關(guān)注點(diǎn) (weaving) 織入到 待完善的關(guān)注點(diǎn)上,實(shí)現(xiàn)單獨(dú)組件無法實(shí)現(xiàn)的功能,以解決面向?qū)ο缶幊淘谀承┓绞较码y于實(shí)現(xiàn)的操作,更好的支持面向?qū)ο蟮拈_關(guān)原則(擴(kuò)展開放,修改關(guān)閉)。
對于三部分:dataSource、transactionManager、事務(wù)代理機(jī)制。無論哪種配置方式,一般變化的都是代理機(jī)制部分。DataSource、TransactionManager這兩部分只是會根據(jù)數(shù)據(jù)訪問方式有所變化。比如使用Hibernate進(jìn)行數(shù)據(jù)訪問時(shí),DataSource實(shí)際為SessionFactory,TransactionManager的實(shí)現(xiàn)為HibernateTransactionManager。
那么我們知道了事務(wù)有五種配置方式和三個(gè)對象,接下來說說它的層面:Spring的事務(wù)到底該給Dao配置還是給Service配置呢?Spring的事務(wù)為業(yè)務(wù)邏輯進(jìn)行事務(wù)管理,保證業(yè)務(wù)邏輯上數(shù)據(jù)的原子性。事務(wù)根據(jù)項(xiàng)目性質(zhì)來細(xì)分:事務(wù)可以設(shè)置到三個(gè)層面(dao層、service層和web層),第一:web層事務(wù),這一般是針對那些安全性要求較高的系統(tǒng)來說的。例如電子商務(wù)網(wǎng)站。粒度小,一般系統(tǒng)用不著這么細(xì)。第二:service層事務(wù),這是一常見的事務(wù)劃分, 將事務(wù)設(shè)置在業(yè)務(wù)邏輯上,只要業(yè)務(wù)邏輯出錯(cuò)或異常就事務(wù)回滾。粒度較小,一般推薦這種方式。第三:數(shù)據(jù)持久層數(shù)據(jù)務(wù),也就是常說的數(shù)據(jù)庫事務(wù)。這種事務(wù)在安全性方面要求低。就是給一個(gè)簡單的增刪改之類的操作增加事務(wù)操作,粒度大。
Spring聲明式事務(wù)讓我們從復(fù)雜的事務(wù)處理中得到解脫。使得我們再也無需要去處理獲得連接、關(guān)閉連接、事務(wù)提交和回滾等這些操作。再也無需要我們在與事務(wù)相關(guān)的方法中處理大量的try…catch…finally代碼。?
我們在使用Spring聲明式事務(wù)時(shí),有一個(gè)非常重要的概念就是事務(wù)屬性。事務(wù)屬性通常由事務(wù)的傳播行為,事務(wù)的隔離級別,事務(wù)的超時(shí)值和事務(wù)只讀標(biāo)志組成。我們在進(jìn)行事務(wù)劃分時(shí),需要進(jìn)行事務(wù)定義,也就是配置事務(wù)的屬性。給Service層配置事務(wù),因?yàn)橐粋€(gè)Service層方法操作可以關(guān)聯(lián)到多個(gè)DAO的操作。在Service層執(zhí)行這些Dao操作,多DAO操作有失敗全部回滾,成功則全部提交。事務(wù)分為業(yè)務(wù)事務(wù)和系統(tǒng)事務(wù),業(yè)務(wù)事務(wù)也就是業(yè)務(wù)邏輯上操作的一致性,系統(tǒng)事務(wù)自然就是指真正的數(shù)據(jù)庫事務(wù),Spring配置事務(wù)的是為了什么進(jìn)行管理,當(dāng)然是為業(yè)務(wù)邏輯進(jìn)行事務(wù)管理,保證業(yè)務(wù)邏輯上數(shù)據(jù)的原子性;Dao層是什么,數(shù)據(jù)訪問層,是不應(yīng)該包含業(yè)務(wù)邏輯的,這就是和Service層的不同;Service層就是業(yè)務(wù)邏輯層,事務(wù)的管理就是為Service層上的保證。
?
接下來,我們看看事務(wù)的定義
// TransactionDefinition public interface TransactionDefinition { int getPropagationBehavior(); //方法 返回事務(wù)的傳播行為,是否由一個(gè)活動(dòng)事務(wù)來決定一個(gè)事務(wù)的調(diào)用。int getTimeout(); //方法 返回事務(wù)必須在多少秒內(nèi)執(zhí)行boolean isReadOnly(); //方法返回事務(wù)是否是只讀的 int getIsolationLevel(); // 返回事務(wù)的隔離級別 } 說說事務(wù)的隔離級別:在TransactionDefinition接口中定義了五個(gè)不同的事務(wù)隔離級別,ISOLATION_DEFAULT?這是一個(gè)默認(rèn)的隔離級別,使用數(shù)據(jù)庫默認(rèn)的事務(wù)隔離級別.另外四個(gè)與JDBC的隔離級別相對應(yīng)。ISOLATION_READ_UNCOMMITTED?這是事務(wù)最低的隔離級別,它充許別外一個(gè)事務(wù)可以看到這個(gè)事務(wù)未提交的數(shù)據(jù)。這種隔離級別會產(chǎn)生臟讀,不可重復(fù)讀和幻像讀。? 例如: Mary的原工資為1000,財(cái)務(wù)人員將Mary的工資改為了8000,但未提交事務(wù) Java代碼 1. Connection con1 = getConnection(); 2. con.setAutoCommit(false); 3. update employee set salary = 8000 where empId ="Mary"; 與此同時(shí),Mary正在讀取自己的工資 Java代碼 1. Connection con2 = getConnection(); 2. select salary from employee where empId ="Mary"; 3. con2.commit(); Mary發(fā)現(xiàn)自己的工資變?yōu)榱?000,歡天喜地! 而財(cái)務(wù)發(fā)現(xiàn)操作有誤,而回滾了事務(wù),Mary的工資又變?yōu)榱?000 Java代碼 1. //con1 2. con1.rollback(); 像這樣,Mary記取的工資數(shù)8000是一個(gè)臟數(shù)據(jù)。 ??
ISOLATION_READ_COMMITTED??保證一個(gè)事務(wù)修改的數(shù)據(jù)提交后才能被另外一個(gè)事務(wù)讀取。另外一個(gè)事務(wù)不能讀取該事務(wù)未提交的數(shù)據(jù)。這種事務(wù)隔離級別可以避免臟讀出現(xiàn),但是可能會出現(xiàn)不可重復(fù)讀和幻像讀。ISOLATION_REPEATABLE_READ??這種事務(wù)隔離級別可以防止臟讀,不可重復(fù)讀。但是可能出現(xiàn)幻像讀。它除了保證一個(gè)事務(wù)不能讀取另一個(gè)事務(wù)未提交的數(shù)據(jù)外,還保證了避免下面的情況產(chǎn)生(不可重復(fù)讀)。? 在事務(wù)1中,Mary 讀取了自己的工資為1000,操作并沒有完成 Java代碼 1. con1 = getConnection(); 2. select salary from employee empId ="Mary"; 在事務(wù)2中,這時(shí)財(cái)務(wù)人員修改了Mary的工資為2000,并提交了事務(wù). Java代碼 1. con2 = getConnection(); 2. update employee set salary = 2000; 3. con2.commit(); 在事務(wù)1中,Mary 再次讀取自己的工資時(shí),工資變?yōu)榱?000 Java代碼 1. //con1 2. select salary from employee empId ="Mary"; 在一個(gè)事務(wù)中前后兩次讀取的結(jié)果并不致,導(dǎo)致了不可重復(fù)讀。 使用ISOLATION_REPEATABLE_READ可以避免這種情況發(fā)生。 ??
ISOLATION_SERIALIZABLE?這是花費(fèi)最高代價(jià)但是最可靠的事務(wù)隔離級別。事務(wù)被處理為順序執(zhí)行。除了防止臟讀,不可重復(fù)讀外,還避免了幻像讀。?@Transactional只能被應(yīng)用到public方法上,?對于其它非public的方法,如果標(biāo)記了@Transactional也不會報(bào)錯(cuò),但方法沒有事務(wù)功能。Spring使用聲明式事務(wù)處理,默認(rèn)情況下,如果被注解的數(shù)據(jù)庫操作方法中發(fā)生了unchecked異常,所有的數(shù)據(jù)庫操作將rollback;如果發(fā)生的異常是checked異常,默認(rèn)情況下數(shù)據(jù)庫操作還是會提交的。這種默認(rèn)的行為是可以改變的。事務(wù)詳細(xì)講解的文章
第一種方式,每個(gè)bean都有一個(gè)代理類。 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"><!-- 數(shù)據(jù)源 --><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&characterEncoding=UTF-8" /><property name="username" value="root" /><property name="password" value="root" /><!-- 連接池啟動(dòng)時(shí)的初始值 --><property name="initialSize" value="10" /><!-- 連接池的最大值 --><property name="maxActive" value="10" /><!-- 最大空閑值.當(dāng)經(jīng)過一個(gè)高峰時(shí)間后,連接池可以慢慢將已經(jīng)用不到的連接慢慢釋放一部分,一直減少到maxIdle為止 --><property name="maxIdle" value="20" /><!-- 最小空閑值.當(dāng)空閑的連接數(shù)少于閥值時(shí),連接池就會預(yù)申請去一些連接,以免洪峰來時(shí)來不及申請 --><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> <!-- 定義事務(wù)管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"><property name="sessionFactory" ref="sessionFactory" /></bean><!-- 配置服務(wù)層 --><bean id="userDaoAgency" class="com.dao.impl.UserDaoImpl"><property name="sessionFactory" ref="sessionFactory" /></bean> <bean id="userDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 配置事務(wù)管理器 --> <property name="transactionManager" ref="transactionManager" /> <property name="target" ref="userDaoAgency" /> <property name="proxyInterfaces" value="com.dao.UserDao" /><!-- 配置事務(wù)屬性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop></props> </property> </bean> </beans> 第二種方式,所有bean共享一個(gè)代理類。 <?xml version="1.0" encoding="UTF-8"?> <beans><!-- 數(shù)據(jù)源 --><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><!-- // 配置同上 --></bean><!-- 會話工廠 --><bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"><!-- // 配置同上 --></bean><!-- 定義事務(wù)管理器 --><bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"><property name="sessionFactory" ref="sessionFactory" /></bean><!-- 定義事務(wù) --><bean id="base" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" lazy-init="true" abstract="true"><!-- 配置事務(wù)管理器 --><property name="transactionManager" ref="transactionManager" /><!-- 配置事務(wù)屬性 --><property name="transactionAttributes"><props><prop key="*">PROPAGATION_REQUIRED</prop></props></property></bean><!-- 配置服務(wù)層 --><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>?第三種方式,使用攔截器。
轉(zhuǎn)載于:https://my.oschina.net/newchaos/blog/1831648
總結(jié)
以上是生活随笔為你收集整理的Spring发展历程总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [转]写好shell脚本的13个技巧
- 下一篇: JavaScript中发布/订阅模式的理