Spring的IOC特性
一. 什么是控制反轉(zhuǎn)
見圖1,軟件中的對(duì)象就如同圖中的齒輪,協(xié)同工作,互相耦合,若是一個(gè)零件不能正常工作,則會(huì)導(dǎo)致整個(gè)系統(tǒng)的奔潰,這就是強(qiáng)耦合系統(tǒng)。為了解決對(duì)象間耦合度過高的問題,軟件專家Michael Mattson提出了IoC理論。
控制反轉(zhuǎn)(Inversion of Control)是一種面向?qū)ο缶幊讨械囊环N設(shè)計(jì)原則,用來(lái)解決計(jì)算機(jī)代碼之間的耦合度。其基本思想是:借助于"第三方"實(shí)現(xiàn)具有依賴關(guān)系的對(duì)象之間的耦合。
見圖2,由于引進(jìn)了IoC容器,使得A、B、C、D這四個(gè)對(duì)象沒有了耦合關(guān)系,對(duì)象的控制權(quán)全部由IoC容器負(fù)責(zé)。
我們?cè)俅蝸?lái)對(duì)比一下:
- 軟件系統(tǒng)在沒有引入IoC容器之前,如圖1,對(duì)象A依賴與對(duì)象B,那么對(duì)象A在初始化或者運(yùn)行到某一點(diǎn)的時(shí)候,自己必須主動(dòng)去創(chuàng)建對(duì)象B或者使用已經(jīng)創(chuàng)建的對(duì)象B。無(wú)論是創(chuàng)建還是使用對(duì)象B,控制權(quán)都在自己手上。
- 軟件系統(tǒng)在引入IoC容器之后,就完全不同了。如圖2,由于IoC容器的加入,對(duì)象A與對(duì)象B之間失去了直接聯(lián)系,所以,當(dāng)對(duì)象A運(yùn)行到需要B的對(duì)象,IoC容器會(huì)主動(dòng)創(chuàng)建一個(gè)對(duì)象B注入到對(duì)象A需要的地方。
通過對(duì)比,可以看出對(duì)象A依賴對(duì)象B的過程,由主動(dòng)行為變成了被動(dòng)行為,控制權(quán)顛倒了過來(lái),這就是"控制反轉(zhuǎn)"的由來(lái)。
二. 什么是依賴注入
當(dāng)A對(duì)象需要調(diào)用B對(duì)象方法時(shí),這種情況在Spring中稱為依賴,即A對(duì)象依賴B對(duì)象,Spring把互相調(diào)用的關(guān)系稱為依賴關(guān)系。
在傳統(tǒng)模式下當(dāng)需要調(diào)用其他對(duì)象的方法時(shí),一般有以下兩種方式:
- 原始做法:調(diào)用者主動(dòng)創(chuàng)建被依賴對(duì)象,然后再調(diào)用被依賴對(duì)象的方法。
- 簡(jiǎn)單工廠模式:調(diào)用者先找到被依賴對(duì)象的工廠,然后主動(dòng)通過工廠去獲取被依賴對(duì)象,最后再調(diào)用被依賴對(duì)象的方法。
對(duì)于第一種方式,由于調(diào)用者需要通過形如"new 被依賴對(duì)象構(gòu)造器();"的代碼來(lái)創(chuàng)建對(duì)象,這種方式會(huì)導(dǎo)致調(diào)用者與被依賴對(duì)象實(shí)現(xiàn)類的硬編碼耦合,不利于項(xiàng)目升級(jí)維護(hù)。
對(duì)于第二種方式,要把握一下三點(diǎn):
- 調(diào)用者面向被依賴對(duì)象的接口編程
- 將被依賴對(duì)象的創(chuàng)建交給工廠完成
- 調(diào)用者通過工廠來(lái)獲得被依賴組件
這樣,調(diào)用者只需與被依賴對(duì)象的接口耦合,這樣就避免了類層次的硬編碼耦合。缺點(diǎn)是,調(diào)用組件需要主動(dòng)通過工廠去獲取被依賴對(duì)象,這就會(huì)帶來(lái)調(diào)用組件與被依賴對(duì)象的耦合。
當(dāng)使用Spring容器后,程序無(wú)須使用new調(diào)用構(gòu)造器去創(chuàng)建對(duì)象,所有的Java對(duì)象都可交給Spring容器去創(chuàng)建;當(dāng)調(diào)用者需要被依賴對(duì)象的方法時(shí),調(diào)用者無(wú)須主動(dòng)獲取被依賴對(duì)象,只需要等待Spring容器注入即可。
三. 控制反轉(zhuǎn)與依賴注入的關(guān)系
- 控制反轉(zhuǎn)是一種思想
- 依賴注入是一種設(shè)計(jì)模式
IoC框架使用依賴注入作為實(shí)現(xiàn)控制反轉(zhuǎn)的方式,但是控制反轉(zhuǎn)還有其他的實(shí)現(xiàn)方式,例如說ServiceLocator,所以不能將控制反轉(zhuǎn)和依賴注入等同。
參考資料
- 《控制反轉(zhuǎn)(IoC)與依賴注入(DI)》
- 《輕量級(jí)JavaEE企業(yè)應(yīng)用實(shí)戰(zhàn)》(第4版)李剛 編著
Spring的AOP特性
以下內(nèi)容來(lái)自于博客Spring AOP 實(shí)現(xiàn)原理與 CGLIB 應(yīng)用
AOP(Aspect Orient Programming),作為面向?qū)ο缶幊痰囊环N補(bǔ)充,廣泛應(yīng)用于處理一些具有橫切性質(zhì)的系統(tǒng)級(jí)服務(wù),如事務(wù)管理、安全檢查、緩存、對(duì)象池管理等。AOP實(shí)現(xiàn)的關(guān)鍵就在于AOP框架自動(dòng)創(chuàng)建的AOP代理,AOP代理則可分為靜態(tài)代理和動(dòng)態(tài)代理兩大類,其中靜態(tài)代理是指AOP框架提供的命令進(jìn)行編譯,從而在編譯階段就可生成AOP代理類,因此也被稱為編譯時(shí)增強(qiáng);而動(dòng)態(tài)代理則是在運(yùn)行時(shí)借助于JDK動(dòng)態(tài)代理、CGLIB等在內(nèi)存中“臨時(shí)”生成AOP動(dòng)態(tài)代理類,因此也被稱為運(yùn)行時(shí)增強(qiáng)。
一. AOP的存在價(jià)值
在傳統(tǒng)的OOP編程里以對(duì)象為核心,整個(gè)軟件系統(tǒng)由一系列相互依賴的對(duì)象組成,而這些對(duì)象將被抽象成一個(gè)個(gè)類,并允許使用類繼承來(lái)管理類與類之間一般到特殊的關(guān)系。隨著軟件規(guī)模的增大,應(yīng)用的逐漸升級(jí),慢慢出現(xiàn)了一些OOP很難解決的問題。
我們可以通過分析、抽象出一系列具有一定屬性與行為的對(duì)象,并通過這些對(duì)象的協(xié)作來(lái)形成一個(gè)完整的軟件功能。由于對(duì)象可以繼承,因此我們可以把具有相同功能或相同特性的屬性抽象到一個(gè)層次分明的類結(jié)構(gòu)體系中。隨著軟件規(guī)范的不斷擴(kuò)大,專業(yè)化分工越來(lái)越系列,以及OOP應(yīng)用實(shí)踐的不斷增多,隨之也暴露出了一些OOP無(wú)法很好解決的問題。
現(xiàn)在假設(shè)系統(tǒng)中有3段完全相似的代碼,這些代碼通常會(huì)采用“復(fù)制”、“粘貼”方式來(lái)完成,通過這種“復(fù)制”、“粘貼”方式開發(fā)出來(lái)的軟件如圖1所示。
看到如圖1所示的示意圖,可以看到了這種設(shè)計(jì)的不足之處。當(dāng)有一天,圖1中的深色代碼段需要修改,那是不是要打開3個(gè)地方的代碼進(jìn)行修改?如果不是3個(gè)地方包含這段代碼,而是100個(gè)地方,甚至是1000個(gè)地方包含這段代碼段,那會(huì)是什么后果?
為了解決這個(gè)問題,我們通常會(huì)采用將如圖1所示的深色代碼部分定義成一個(gè)方法,然后在3個(gè)代碼段中分別調(diào)用該方法即可。在這種方式下,軟件系統(tǒng)的結(jié)構(gòu)如圖2所示。
對(duì)于如圖2所示的軟件系統(tǒng),如果需要修改深色部分的代碼,只要修改一個(gè)地方即可,不管整個(gè)系統(tǒng)中有多少方法調(diào)用了該方法,程序無(wú)須修改這些地方,只需要修改被調(diào)用的方法即可——通過這種方式,大大降低了軟件后期維護(hù)的復(fù)雜度。
對(duì)于如圖2所示的方法1、方法2、方法3依然需要顯式調(diào)用深色方法,這樣做能夠解決大部分應(yīng)用場(chǎng)景。但對(duì)于一些更特殊的情況:應(yīng)用需要方法1、方法2、方法3徹底與深色方法分離——方法1、方法2、方法3無(wú)須直接調(diào)用深色方法,該如何解決?
因?yàn)檐浖到y(tǒng)需求變更是非常頻繁的事情,系統(tǒng)前期設(shè)計(jì)方法1、方法2、方法3時(shí)只實(shí)現(xiàn)了核心業(yè)務(wù)功能,過了一段時(shí)間,我們需要為方法1、方法2、方法3都增加事務(wù)控制;又過了一段時(shí)間,客戶提出方法1、方法2、方法3需要進(jìn)行用戶合法性驗(yàn)證。只有合法的用戶才能執(zhí)行這些方法。因此,我們希望有一種特殊的方法:我們只要定義該方法,無(wú)須在方法1、方法2、方法3中顯式調(diào)用它,系統(tǒng)會(huì)“自動(dòng)”執(zhí)行該特殊方法。
實(shí)現(xiàn)上述需求的技術(shù)就是AOP。AOP專門用于處理系統(tǒng)中分布于各個(gè)模塊(不同方法)中交叉關(guān)注點(diǎn)的問題,在JavaEE應(yīng)用中,常常通過AOP來(lái)處理一些具有橫切性質(zhì)的系統(tǒng)級(jí)服務(wù),如事務(wù)管理、安全檢查、緩存、對(duì)象池管理等,AOP已經(jīng)成為一種非常常用的解決方案。
二. Spring AOP原理剖析
Spring AOP框架對(duì)AOP代理類的處理原則是:如果目標(biāo)對(duì)象的實(shí)現(xiàn)類實(shí)現(xiàn)了接口,Spring AOP將會(huì)采用JDK動(dòng)態(tài)代理來(lái)生成AOP代理類;如果目標(biāo)對(duì)象的實(shí)現(xiàn)類沒有實(shí)現(xiàn)接口,Spring AOP將會(huì)采用CGLIB來(lái)生成AOP代理類——不過這個(gè)選擇過程對(duì)開發(fā)者完全透明、開發(fā)者無(wú)須關(guān)心。
AOP代理其實(shí)是由AOP框架動(dòng)態(tài)生成的一個(gè)對(duì)象,該對(duì)象可作為目標(biāo)對(duì)象使用。AOP代理包含了目標(biāo)對(duì)象的全部方法,但AOP代理中的方法與目標(biāo)對(duì)象的方法存在差異:AOP方法在特定切入點(diǎn)添加了增強(qiáng)處理,并回調(diào)了目標(biāo)對(duì)象的方法。
AOP代理所包含的方法與目標(biāo)對(duì)象的方法示意圖如圖3所示。
Spring的AOP代理有Spring的IoC容器負(fù)責(zé)生成、管理,其依賴關(guān)系也有IoC容器負(fù)責(zé)管理。因此,AOP代理可以直接使用容器中的其他Bean實(shí)例作為目標(biāo),這種關(guān)系可由IoC容器的依賴注入提供。
縱觀AOP編程,其中需要程序員參與的只有3個(gè)部分:
- 定義普通業(yè)務(wù)組件
- 定義切入點(diǎn),一個(gè)切入點(diǎn)可能橫切多個(gè)業(yè)務(wù)組件
- 定義增強(qiáng)處理,增強(qiáng)處理就是在AOP框架為普通業(yè)務(wù)組件織入的處理動(dòng)作
上面3個(gè)部分的第一個(gè)部分是最平常不過的事情,無(wú)須額外說明。那么進(jìn)行AOP編程的關(guān)鍵就是定義切入點(diǎn)和定義增強(qiáng)處理。一但定義了合適的切入點(diǎn)和增強(qiáng)處理,AOP框架將會(huì)自動(dòng)生成AOP代理,而AOP代理的方法大致有如下公式:
代理對(duì)象的方法 = 增強(qiáng)處理 + 被代理對(duì)象的方法
Spring AOP的實(shí)現(xiàn)原理:AOP框架負(fù)責(zé)動(dòng)態(tài)生成AOP代理類,這個(gè)代理類的方法則由Advice和回調(diào)方法對(duì)象的方法所組成。
對(duì)于前面提到的圖2所示的軟件調(diào)用結(jié)構(gòu):當(dāng)方法1、方法2、方法3......都需要去調(diào)用某個(gè)具有“橫切”性質(zhì)的方法時(shí),傳統(tǒng)的做法是程序員去手動(dòng)修改方法1、方法2、方法3......通過代碼來(lái)調(diào)用這個(gè)具有“橫切”性質(zhì)的方法,但這種做法的可擴(kuò)展性不好,因?yàn)槊看味家薷拇a。
于是AOP框架出現(xiàn),AOP框架則可以“動(dòng)態(tài)的”生成一個(gè)新的代理類,而這個(gè)代理類所包含的方法1、方法2、方法3的代碼,程序員只要定義切入點(diǎn)即可——AOP框架所生成的AOP代理類中包含了新的方法1、方法2、方法3,而AOP框架會(huì)根據(jù)切入點(diǎn)來(lái)決定是否要在方法1、方法2、方法3中回調(diào)具有“橫切”性質(zhì)的方法。
簡(jiǎn)而言之:AOP原理的奧妙就在于動(dòng)態(tài)地生成了代理類,這個(gè)代理類實(shí)現(xiàn)了圖2的調(diào)用——這種調(diào)用無(wú)須程序員修改代碼。
參考資料
- 《輕量級(jí)JavaEE企業(yè)應(yīng)用實(shí)戰(zhàn)》(第4版)李剛 編著
- 《瘋狂Java講義》李剛 編著