前言
前文已經介紹了Spring Bean的生命周期,在這個周期內有一個重要的概念就是: IOC容器
大家也知道IOC是Sping 的重要核心之一,那么如何理解它呢,它又是產生什么作用呢?本文就IOC原理進行簡要闡述。
IOC定義
IoC 全稱為 Inversion of Control
,翻譯為 “控制反轉”,它還有一個別名為 DI(Dependency Injection
),即依賴注入。
DI—Dependency Injection,即“依賴注入”:組件之間依賴關系由容器在運行期決定,形象的說,即由容器動態的將某個依賴關系注入到組件之中。
依賴注入的目的並非為軟件系統帶來更多功能,而是為了提升組件重用的頻率,並為系統搭建一個靈活、可擴展的平台。
通過依賴注入機制,我們只需要通過簡單的配置,而無需任何代碼就可指定目標需要的資源,完成自身的業務邏輯,而不需要關心具體的資源來自何處,由誰實現。
如何理解“控制反轉”好呢?理解好它的關鍵在於我們需要回答如下四個問題:
- 誰控制誰
- 控制什么
- 為何是反轉
- 哪些方面反轉了
IoC和DI由什么關系呢?其實它們是同一個概念的不同角度描述,由於控制反轉概念比較含糊(可能只是理解為容器控制對象這一個層面,很難讓人想到誰來維護對象關系),所以2004年大師級人物Martin Fowler又給出了一個新的名字:“依賴注入”,相對IoC 而言,“依賴注入”明確描述了“被注入對象依賴IoC容器配置依賴對象”。
理解IOC
以年輕小伙子找女朋友為例子來說明IOC的作用:
/** * 年輕小伙子 */ public class YoungMan { private BeautifulGirl beautifulGirl; YoungMan(){ // 可能你比較牛逼,指腹為婚 // beautifulGirl = new BeautifulGirl(); } public void setBeautifulGirl(BeautifulGirl beautifulGirl) { this.beautifulGirl = beautifulGirl; } public static void main(String[] args){ YoungMan you = new YoungMan(); BeautifulGirl beautifulGirl = new BeautifulGirl("你的各種條件"); beautifulGirl.setxxx("各種投其所好"); // 然后你有女票了 you.setBeautifulGirl(beautifulGirl); } }
不使用IOC:
小伙子需要: new BeautifulGirl() ,也就是自己去創建一個女朋友對象。這個過程復雜而又繁瑣,而且我們必須要面對每個環節,同時使用完成之后我們還要負責銷毀它。
使用IOC:
小伙子自己不用去找女朋友,反過來找IOC,IOC就相當於一個婚介公司,它管理着很多男男女女的資料,小伙子直接跟婚介公司提出需求,婚介公司則會根據需求提供一個妹子給我們,我們只需要負責使用它就行了。
所以,簡單點說,IoC 的理念就是讓別人為你服務
可以理解為: IOC主動把妹子注入給想使用它的小伙子。 (調用的時候使用Autowied ,這個對象就是前文說的bean, 通過注冊進入IOC容器,被實例化之后再進入IOC容器的bean緩存池,就可以供程序調用了,這就和bean的生命周期連了起來。)
通過IOC的注冊機制可以保證對象的安全性和合規性;
實例化對象只需要實例化一次,即可進入IOC容器的bean緩存池,降低了對象的創建開銷,提高了程序的性能(有點類似單例);
應用程序調用對象從bean緩存池獲取,這樣是秒獲取對象,提高了調用對象的速度。
現在來回答上面那四個問題,答案就顯得非常明顯了:
- 誰控制誰:在傳統的開發模式下,我們都是采用直接 new 一個對象的方式來創建對象,也就是說你依賴的對象直接由你自己控制,但是有了 IOC 容器后,則直接由 IoC 容器來控制。所以“誰控制誰”,當然是 IoC 容器控制對象。
- 控制什么:控制對象。
- 為何是反轉:沒有 IoC 的時候我們都是在自己對象中主動去創建被依賴的對象,這是正轉。但是有了 IoC 后,所依賴的對象直接由 IoC 容器創建后注入到被注入的對象中,依賴的對象由原來的主動獲取變成被動接受,所以是反轉。
- 哪些方面反轉了:所依賴對象的獲取被反轉了。
IOC提供被依賴對象的方式
IOC Service Provider 為被注入對象提供被依賴對象也有如下幾種方式:構造方法注入、stter方法注入、接口注入。
構造器注入
構造器注入,顧名思義就是被注入的對象通過在其構造方法中聲明依賴對象的參數列表,讓外部知道它需要哪些依賴對象。
YoungMan(BeautifulGirl beautifulGirl){ this.beautifulGirl = beautifulGirl; //這里可以定義很多女孩的條件 }
構造器注入方式比較直觀,對象構造完畢后就可以直接使用,這就好比你出生你家里就給你指定了你媳婦。
setter 方法注入
對於 JavaBean 對象而言,我們一般都是通過 getter 和 setter 方法來訪問和設置對象的屬性。所以,當前對象只需要為其所依賴的對象提供相對應的 setter 方法,就可以通過該方法將相應的依賴對象設置到被注入對象中。如下:
public class YoungMan { private BeautifulGirl beautifulGirl; public void setBeautifulGirl(BeautifulGirl beautifulGirl) { this.beautifulGirl = beautifulGirl; } }
相比於構造器注入,setter 方式注入會顯得比較寬松靈活些,它可以在任何時候進行注入(當然是在使用依賴對象之前),這就好比你可以先把自己想要的妹子想好了,然后再跟婚介公司打招呼,你可以要林志玲款式的,趙麗穎款式的,隨意性較強。
一般程序中會使用這種方法進行實例化。
接口方式注入
接口方式注入顯得比較霸道,因為它需要被依賴的對象實現不必要的接口,帶有侵入性。一般都不推薦這種方式。
總結:
A對象需要使用合作對象B來共同完成一件事,A要使用B,那么A就對B產生了依賴,也就是A和B之間存在一種耦合關系,並且是緊密耦合在一起。
而使用了Spring之后就不一樣了,創建合作對象B的工作是由Spring來做的,Spring創建好B對象,然后存儲到一個容器里面,當A對象需要使用B對象時,Spring就從存放對象的那個容器里面取出A要使用的那個B對象,然后交給A對象使用,至於Spring是如何創建那個對象,以及什么時候創建好對象的,A對象不需要關心這些細節問題(你是什么時候生的,怎么生出來的我可不關心,能幫我干活就行),A得到Spring給我們的對象之后,兩個人一起協作完成要完成的工作即可。
所以控制反轉IoC(Inversion of Control)是說創建對象的控制權進行轉移,以前創建對象的主動權和創建時機是由自己把控的,而現在這種權力轉移到第三方,比如轉移交給了IoC容器,它就是一個專門用來創建對象的工廠,你要什么對象,它就給你什么對象,有了 IoC容器,依賴關系就變了,原先的依賴關系就沒了,它們都依賴IoC容器了,通過IoC容器來建立它們之間的關系。