理論:第二章:Spring的AOP和IOC是什么?使用場(chǎng)景有哪些?Spring事務(wù)與數(shù)據(jù)庫(kù)事務(wù),傳播行為,數(shù)據(jù)庫(kù)隔離級(jí)別
AOP:面向切面編程。
即在一個(gè)功能模塊中新增其他功能,比方說(shuō)你要下樓取個(gè)快遞,你同事對(duì)你說(shuō)幫我也取一下唄,你就順道取了。在工作中如果系統(tǒng)中有些包和類(lèi)中沒(méi)有使用AOP,例如日志,事務(wù)和異常處理,那么就必須在每個(gè)類(lèi)和方法中去實(shí)現(xiàn)它們。 代碼糾纏每個(gè)類(lèi)和方法中都包含日志,事務(wù)以及異常處理甚至是業(yè)務(wù)邏輯。在一個(gè)這樣的方法中,很難分清代碼中實(shí)際做的是什么處理。AOP 所做的就是將所有散落各處的事務(wù)代碼集中到一個(gè)事務(wù)切面中。
場(chǎng)景
比方說(shuō)我現(xiàn)在要弄一個(gè)日志,記錄某些個(gè)接口調(diào)用的方法時(shí)間。使用Aop我可以在這個(gè)接口前插入一段代碼去記錄開(kāi)始時(shí)間,在這個(gè)接口后面去插入一段代碼記錄結(jié)束時(shí)間。
又或者你去訪問(wèn)數(shù)據(jù)庫(kù),而你不想管事務(wù)(太煩),所以,Spring在你訪問(wèn)數(shù)據(jù)庫(kù)之前,自動(dòng)幫你開(kāi)啟事務(wù),當(dāng)你訪問(wèn)數(shù)據(jù)庫(kù)結(jié)束之后,自動(dòng)幫你提交/回滾事務(wù)!
異常處理你可以開(kāi)啟環(huán)繞通知,一旦運(yùn)行接口報(bào)錯(cuò),環(huán)繞通知捕獲異常跳轉(zhuǎn)異常處理頁(yè)面。
動(dòng)態(tài)代理
Spring AOP使用的動(dòng)態(tài)代理,所謂的動(dòng)態(tài)代理就是說(shuō)AOP框架不會(huì)去修改字節(jié)碼,而是在內(nèi)存中臨時(shí)為方法生成一個(gè)AOP對(duì)象,這個(gè)AOP對(duì)象包含了目標(biāo)對(duì)象的全部方法,并且在特定的切點(diǎn)做了增強(qiáng)處理,并回調(diào)原對(duì)象的方法。它的動(dòng)態(tài)代理主要有兩種方式,JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理。JDK動(dòng)態(tài)代理通過(guò)反射來(lái)接收被代理的類(lèi),并且要求被代理的類(lèi)必須實(shí)現(xiàn)一個(gè)接口。JDK動(dòng)態(tài)代理的核心是InvocationHandler接口和Proxy類(lèi)。如果目標(biāo)類(lèi)沒(méi)有實(shí)現(xiàn)接口,那么Spring AOP會(huì)選擇使用CGLIB來(lái)動(dòng)態(tài)代理目標(biāo)類(lèi)。CGLIB是一個(gè)代碼生成的類(lèi)庫(kù),可以在運(yùn)行時(shí)動(dòng)態(tài)的生成某個(gè)類(lèi)的子類(lèi),注意,CGLIB是通過(guò)繼承的方式做的動(dòng)態(tài)代理,因此如果某個(gè)類(lèi)被標(biāo)記為final,那么它是無(wú)法使用CGLIB做動(dòng)態(tài)代理的。
IOC:依賴(lài)注入或者叫做控制反轉(zhuǎn)。
正常情況下我們使用一個(gè)對(duì)象時(shí)都是需要new Object()的。而ioc是把需要使用的對(duì)象提前創(chuàng)建好,放到spring的容器里面。
所有需要使用的類(lèi)都會(huì)在spring容器中登記,告訴spring你是個(gè)什么東西,你需要什么東西,然后spring會(huì)在系統(tǒng)運(yùn)行到適當(dāng)?shù)臅r(shí)候,把你要的東西主動(dòng)給你,同時(shí)也把你交給其他需要你的東西。所有的類(lèi)的創(chuàng)建、銷(xiāo)毀都由 spring來(lái)控制,也就是說(shuō)控制對(duì)象生存周期的不再是引用它的對(duì)象,而是spring。DI(依賴(lài)注入)其實(shí)就是IOC的一種實(shí)現(xiàn)方式。
場(chǎng)景:
正常情況下我們使用一個(gè)對(duì)象時(shí)都是需要new Object() 的。而ioc是把需要使用的對(duì)象提前創(chuàng)建好,放到spring的容器里面。需要使用的時(shí)候直接使用就行,而且可以設(shè)置單例或多例,非常靈活。
我們?cè)趕ervice層想調(diào)用另外一個(gè)service的方法,不需要去new了,直接把它交給spring管理,然后用注解的方式引入就能使用。
IOC三種注入方式
(1)XML:Bean實(shí)現(xiàn)類(lèi)來(lái)自第三方類(lèi)庫(kù),例如DataSource等。需要命名空間等配置,例如:context,aop,mvc。
(2)注解:在開(kāi)發(fā)的類(lèi)使用@Controller,@Service等注解
(3)Java配置類(lèi):通過(guò)代碼控制對(duì)象創(chuàng)建邏輯的場(chǎng)景。例如:自定義修改依賴(lài)類(lèi)庫(kù)。
什么是事務(wù)?
事務(wù)是訪問(wèn)并可能更新數(shù)據(jù)庫(kù)中各種數(shù)據(jù)項(xiàng)的一個(gè)程序執(zhí)行單元。
Spring事務(wù)與數(shù)據(jù)庫(kù)事務(wù)關(guān)系?
Spring的事務(wù)是對(duì)數(shù)據(jù)庫(kù)的事務(wù)的封裝,最后本質(zhì)的實(shí)現(xiàn)還是在數(shù)據(jù)庫(kù),假如數(shù)據(jù)庫(kù)不支持事務(wù)的話(huà),Spring的事務(wù)是沒(méi)有作用的。所以說(shuō)Spring事務(wù)的底層依賴(lài)MySQL的事務(wù),Spring是在代碼層面利用AOP實(shí)現(xiàn),執(zhí)行事務(wù)的時(shí)候使用TransactionInceptor進(jìn)行攔截,然后處理。本質(zhì)是對(duì)方法前后進(jìn)行攔截,然后在目標(biāo)方法開(kāi)始之前創(chuàng)建或者加入一個(gè)事務(wù),執(zhí)行完目標(biāo)方法之后根據(jù)執(zhí)行的情況提交或者回滾。
屬性(特性)
A(原子性):要么全部完成,要么完全不起作用
C(一致性):一旦事務(wù)完成(不管成功還是失?。?,業(yè)務(wù)處于一致的狀態(tài),而不會(huì)是部分完成,部分失敗。
I(隔離性):多事務(wù)會(huì)同時(shí)處理相同的數(shù)據(jù),因此每個(gè)事務(wù)都應(yīng)該與其他事務(wù)隔離開(kāi)來(lái),防止數(shù)據(jù)損壞。
D(持久性):一旦事務(wù)完成,無(wú)論發(fā)生什么系統(tǒng)錯(cuò)誤,它的結(jié)果都不應(yīng)該受到影響,事務(wù)的結(jié)果被寫(xiě)到持久化存儲(chǔ)器中。
什么叫事務(wù)傳播行為?
傳播,至少有兩個(gè)東西,才可以發(fā)生傳播。單體不存在傳播這個(gè)行為。事務(wù)傳播行為就是當(dāng)一個(gè)事務(wù)方法被另一個(gè)事務(wù)方法調(diào)用時(shí),這個(gè)事務(wù)方法應(yīng)該如何進(jìn)行。
Spring支持7中事務(wù)傳播行為
propagation_required(需要傳播):當(dāng)前沒(méi)有事務(wù)則新建事務(wù),有則加入當(dāng)前事務(wù)
propagation_supports(支持傳播):支持當(dāng)前事務(wù),如果當(dāng)前沒(méi)有事務(wù)則以非事務(wù)方式執(zhí)行
propagation_mandatory(強(qiáng)制傳播):使用當(dāng)前事務(wù),如果沒(méi)有則拋出異常
propagation_nested(嵌套傳播):如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行,如果當(dāng)前沒(méi)有事務(wù),則執(zhí)行需要傳播行為。
propagation_never(絕不傳播):以非事務(wù)的方式執(zhí)行,如果當(dāng)前有事務(wù)則拋出異常
propagation_requires_new(傳播需要新的):新建事務(wù),如果當(dāng)前有事務(wù)則把當(dāng)前事務(wù)掛起
propagation_not_supported(不支持傳播):以非事務(wù)的方式執(zhí)行,如果當(dāng)前有事務(wù)則把當(dāng)前事務(wù)掛起
關(guān)于事務(wù)的傳播行為,個(gè)人覺(jué)得這篇寫(xiě)的非常好,這里給上鏈接:https://www.cnblogs.com/haha12/p/11855001.html
數(shù)據(jù)庫(kù)事務(wù)的隔離級(jí)別
數(shù)據(jù)庫(kù)事務(wù)的隔離級(jí)別有4個(gè),由低到高依次為Read uncommitted、Read committed、Repeatable read、Serializable,這四個(gè)級(jí)別可以逐個(gè)解決臟讀、不可重復(fù)讀、幻讀這幾類(lèi)問(wèn)題。
√: 可能出現(xiàn) ×: 不會(huì)出現(xiàn)
注意:我們討論隔離級(jí)別的場(chǎng)景,主要是在多個(gè)事務(wù)并發(fā)的情況下,因此,接下來(lái)的講解都圍繞事務(wù)并發(fā)。
Read uncommitted 讀未提交
公司發(fā)工資了,領(lǐng)導(dǎo)把20000元打到廖志偉的賬號(hào)上,但是該事務(wù)并未提交,而廖志偉正好去查看賬戶(hù),發(fā)現(xiàn)工資已經(jīng)到賬,是20000元整,非常高興??墒遣恍业氖牵I(lǐng)導(dǎo)發(fā)現(xiàn)發(fā)給廖志偉的工資金額不對(duì),是16000元,于是迅速修改金額,將事務(wù)提交,最后廖志偉實(shí)際的工資只有16000元,廖志偉空歡喜一場(chǎng)。
出現(xiàn)上述情況,即我們所說(shuō)的臟讀,兩個(gè)并發(fā)的事務(wù),“事務(wù)A:領(lǐng)導(dǎo)給廖志偉發(fā)工資”、“事務(wù)B:廖志偉查詢(xún)工資賬戶(hù)”,事務(wù)B讀取了事務(wù)A尚未提交的數(shù)據(jù)。當(dāng)隔離級(jí)別設(shè)置為Read uncommitted時(shí),就可能出現(xiàn)臟讀,如何避免臟讀,請(qǐng)看下一個(gè)隔離級(jí)別。
Read committed 讀提交
廖志偉拿著工資卡去消費(fèi),系統(tǒng)讀取到卡里確實(shí)有2000元,而此時(shí)她的老婆也正好在網(wǎng)上轉(zhuǎn)賬,把廖志偉工資卡的2000元轉(zhuǎn)到另一賬戶(hù),并在廖志偉之前提交了事務(wù),當(dāng)廖志偉扣款時(shí),系統(tǒng)檢查到廖志偉的工資卡已經(jīng)沒(méi)有錢(qián),扣款失敗,廖志偉十分納悶,明明卡里有錢(qián),為何…
出現(xiàn)上述情況,即我們所說(shuō)的不可重復(fù)讀,兩個(gè)并發(fā)的事務(wù),“事務(wù)A:廖志偉消費(fèi)”、“事務(wù)B:廖志偉的老婆網(wǎng)上轉(zhuǎn)賬”,事務(wù)A事先讀取了數(shù)據(jù),事務(wù)B緊接了更新了數(shù)據(jù),并提交了事務(wù),而事務(wù)A再次讀取該數(shù)據(jù)時(shí),數(shù)據(jù)已經(jīng)發(fā)生了改變。當(dāng)隔離級(jí)別設(shè)置為Read committed時(shí),避免了臟讀,但是可能會(huì)造成不可重復(fù)讀。大多數(shù)數(shù)據(jù)庫(kù)的默認(rèn)級(jí)別就是Read committed,比如Sql Server , Oracle。如何解決不可重復(fù)讀這一問(wèn)題,請(qǐng)看下一個(gè)隔離級(jí)別。
Repeatable read 重復(fù)讀
當(dāng)廖志偉拿著工資卡去消費(fèi)時(shí),一旦系統(tǒng)開(kāi)始讀取工資卡信息(即事務(wù)開(kāi)始),廖志偉的老婆就不可能對(duì)該記錄進(jìn)行修改,也就是廖志偉的老婆不能在此時(shí)轉(zhuǎn)賬。這就避免了不可重復(fù)讀。廖志偉的老婆工作在銀行部門(mén),她時(shí)常通過(guò)銀行內(nèi)部系統(tǒng)查看廖志偉的信用卡消費(fèi)記錄。有一天,她正在查詢(xún)到廖志偉當(dāng)月信用卡的總消費(fèi)金額(select
sum(amount) from transaction where month =
本月)為80元,而廖志偉此時(shí)正好在外面胡吃海喝后在收銀臺(tái)買(mǎi)單,消費(fèi)1000元,即新增了一條1000元的消費(fèi)記錄(insert
transaction …
),并提交了事務(wù),隨后廖志偉的老婆將廖志偉當(dāng)月信用卡消費(fèi)的明細(xì)打印到A4紙上,卻發(fā)現(xiàn)消費(fèi)總額為1080元,廖志偉的老婆很詫異,以為出現(xiàn)了幻覺(jué),幻讀就這樣產(chǎn)生了。當(dāng)隔離級(jí)別設(shè)置為Repeatable
read時(shí),可以避免不可重復(fù)讀,但會(huì)出現(xiàn)幻讀。注:MySQL的默認(rèn)隔離級(jí)別就是Repeatable read。
Serializable 序列化
Serializable是最高的事務(wù)隔離級(jí)別,同時(shí)代價(jià)也花費(fèi)最高,性能很低,一般很少使用,在該級(jí)別下,事務(wù)順序執(zhí)行,不僅可以避免臟讀、不可重復(fù)讀,還避免了幻像讀。