Spring揭祕-IOC介紹

【版權申明】未經博主同意,謝絕轉載!(請尊重原創,博主保留追究權)
https://blog.csdn.net/qq_36000403/article/details/83155133
出自【zzf__的博客】

1.IOC理念:讓別人爲你服務

IOC的全稱是Inversion of Control即控制反轉 還有一個別名叫做依賴注入

1.1構造依賴對象的傳統做法:

public FXNewsProvider(){
	newListener = new DowJonesNewsListener();
	newPersistener = new DowJonesNewsPersister();
}

2.2 IoC的引出
傳統做法是通過new構造對象 或者是通過Service-Locator(可以解決直接的依賴耦合),它們都有一個共同點,就是我們都是主動地去獲取依賴的對象,其實沒有必要這樣,我們最終需要做的,其實就是直接調用依賴對象所提供的某項服務而已,只要用到這個依賴對象的時候,它能準備就緒,我們完全可以不管這個對象是自己找來的還是別人送來的。如果有人能夠在我們需要時將某個依賴對象送過來,爲什麼還要大費周折地自己去折騰?IOC就爲我們做了這樣的事,他的反轉,就反轉在讓你原來的事必躬親,轉變爲現在的享受服務。IOC的理念就是讓別人爲你服務!!
如下圖所示:

所有的被注入對象和依賴對象都由IoC Service Provider統一管理 被注入對象需要什麼,直接跟IoC Service Provider招呼一聲,後者就會把相應的被依賴對象注入到被注入對象中,從而達到IoC Service Provider爲被注入對象服務的目的 IoC Service Provider在這裏就是通常的IoC容器所充當到的角色

從被注入對象的角度看,與之前直接尋求依賴對象相比,依賴對象的取得方式發生了反轉,控制也從被注入對象轉到了IoC Service Provider那裏


2.加深理解IoC

如果還是有點不理解IoC的設計理念,我今天和大家分享網上的一些技術大牛們對Spring框架的IOC的理解。

以下內容來自:http://luanxiyuan.iteye.com/blog/2279954
一、分享Iteye的開濤對Ioc的精彩講解
首先要分享的是Iteye的開濤這位技術牛人對Spring框架的IOC的理解,寫得非常通俗易懂, 以下內容全部來自原文,原文地址:http://jinnianshilongnian.iteye.com/blog/1413846
 
2.1、IoC是什麼
Ioc—Inversion of Control,即“控制反轉”,不是什麼技術,而是是一種設計思想。在Java開發中,Ioc意味着將你設計好的對象交給容器控制,而不是傳統的在你的對象內部直接控制。如何理解好Ioc呢?理解好Ioc的關鍵是要明確“誰控制誰,控制什麼,爲何是反轉(有反轉就應該有正轉了),哪些方面反轉了”,那我們來深入分析一下:

  • 誰控制誰,控制什麼:
    傳統JavaSE程序設計,我們直接在對象內部通過new進行創建對象,是程序主動去創建依賴對象;而IoC是有專門一個容器來創建這些對象,即由Ioc容器來控制對象的創建;誰控制誰?當然是IoC 容器控制了對象;控制什麼?那就是主要控制了外部資源獲取(不只是對象包括比如文件等)。

  • 爲何是反轉,哪些方面反轉了
    有反轉就有正轉,傳統應用程序是由我們自己在對象中主動控制去直接獲取依賴對象,也就是正轉;而反轉則是由容器來幫忙創建及注入依賴對象;爲何是反轉?因爲由容器幫我們查找及注入依賴對象,對象只是被動的接受依賴對象,所以是反轉;哪些方面反轉了?依賴對象的獲取被反轉了。

    用圖例說明一下,傳統程序如下圖,都是主動去創建相關對象然後再組合起來:

在這裏插入圖片描述

當有了IoC/DI的容器後,在客戶端類中不再主動去創建這些對象了,如下圖所示:
在這裏插入圖片描述

2.2 IoC能做什麼

IoC 不是一種技術,只是一種思想,一個重要的面向對象編程的法則,它能指導我們如何設計出鬆耦合、更優良的程序。傳統應用程序都是由我們在類內部主動創建依賴對象,從而導致類與類之間高耦合,難於測試;有了IoC容器後,把創建和查找依賴對象的控制權交給了容器,由容器進行注入組合對象,所以對象與對象之間是 鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程序的整個體系結構變得非常靈活。

其實IoC對編程帶來的最大改變不是從代碼上,而是從思想上,發生了“主從換位”的變化。應用程序原本是老大,要獲取什麼資源都是主動出擊,但是在IoC/DI思想中,應用程序就變成被動的了,被動的等待IoC容器來創建並注入它所需要的資源了。

IoC很好的體現了面向對象設計法則之一—— 好萊塢法則:“別找我們,我們找你”;即由IoC容器幫對象找相應的依賴對象並注入,而不是由對象主動去找。

2.3 IoC和DI

DI—Dependency Injection,即“依賴注入”:組件之間依賴關係由容器在運行期決定,形象的說,即由容器動態的將某個依賴關係注入到組件之中。依賴注入的目的並非爲軟件系統帶來更多功能,而是爲了提升組件重用的頻率,併爲系統搭建一個靈活、可擴展的平臺。通過依賴注入機制,我們只需要通過簡單的配置,而無需任何代碼就可指定目標需要的資源,完成自身的業務邏輯,而不需要關心具體的資源來自何處,由誰實現。

理解DI的關鍵是:“誰依賴誰,爲什麼需要依賴,誰注入誰,注入了什麼”,那我們來深入分析一下:

  • 誰依賴於誰:當然是應用程序依賴於IoC容器;

  • 爲什麼需要依賴:應用程序需要IoC容器來提供對象需要的外部資源;

  • 誰注入誰:很明顯是IoC容器注入應用程序某個對象,應用程序依賴的對象;

  • 注入了什麼:就是注入某個對象所需要的外部資源(包括對象、資源、常量數據)。

IoC和DI由什麼關係呢?其實它們是同一個概念的不同角度描述由於控制反轉概念比較含糊(可能只是理解爲容器控制對象這一個層面,很難讓人想到誰來維護對象關係),所以2004年大師級人物Martin Fowler又給出了一個新的名字:依賴注入”,相對IoC 而言,“依賴注入”明確描述了“被注入對象依賴IoC容器配置依賴對象”。

看過很多對Spring的Ioc理解的文章,好多人對Ioc和DI的解釋都晦澀難懂,反正就是一種說不清,道不明的感覺,讀完之後依然是一頭霧水,感覺就是開濤這位技術牛人寫得特別通俗易懂,他清楚地解釋了IoC(控制反轉) 和DI(依賴注入)中的每一個字,讀完之後給人一種豁然開朗的感覺。我相信對於初學Spring框架的人對Ioc的理解應該是有很大幫助的。

2.4 最後總結一下:
控制反轉IoC(Inversion of Control)是說將創建對象的控制權進行轉移,以前創建對象的主動權和創建時機是由自己把控的,而現在這種權力轉移到第三方,比如轉移交給了IoC容器,它就是一個專門用來創建對象的工廠,你要什麼對象,它就給你什麼對象,有了 IoC容器,依賴關係就變了,原先的依賴關係就沒了,它們都依賴IoC容器了,通過IoC容器來建立它們之間的關係

IOC:控制反轉:將對象的創建權,由Spring管理.
DI:依賴注入:在Spring創建對象的過程中,把對象依賴的屬性注入到對象中.被注入對象依賴IoC容器配置依賴對象


3.被注入對象通知IoC Service Provider 的三種方式

想讓IoC Service Provider爲被注入對象提供服務,並將所需要的被依賴對象送過來,也需要通過某種方式通知對方。要其中提到了有三種依賴注入方式即構造方法注入,setter方法注入,以及接口注入

三種方式的優缺點:

3.1 接口注入:

  • 不作過多介紹,採用這種用法很少很少;
    從注入方式的使用上來說,接口注入是現在不甚提倡的一種方式,處於”退役狀態”。因爲它強制被注入對象實現不必要的接口,帶有侵入性。而構造方法注入和setter方法注入則不需要如此

3.2 構造方法注入:

public FXNewsProvider(IFXNewsListener newsListner, IFXNewsPersister newsPersister){
	this.newsListner =  newsListner;
	this.newsPersister  =  newsPersister;
}
  • 構造方法注入:
    被注入對象的構造乃至其整個生命週期,應該都由IoC Service
    Provider來管理的,這種注入方式的優點就是,對象在構造完成之後,即已進入就緒狀態,可以馬上使用。缺點就是,①當依賴對象比較多的時候,構造方法的參數列表會比較長。②而通過反射構造對象的時候,對相同類型的參數的處理會比較困難,維護和使用上也比較麻煩。③而且在JAVA中,構造方法無法被繼承,無法設置默認值。④對於非必須的依賴處理,可能需要引入多個構造方法,而參數數量的變動可能造成維護上的不便。

3.3 setter方法注入:

public class FXNewsProvider{
	private IFXNewsListener newsListener;
	private IFXNewsPersister newPersistener;

	public IFXNewsListener  getNewsListener(){
			return newsListener;
	}
	public void setNewsListener(IFXNewsListener  newsListener){
			this.newsListener = newsListener;
	}
	public IFXNewsListener  getNewPersistener(){
			return newPersistener;
	}
	public void getNewsListener(IFXNewsPersister newPersistener){
			this.newPersistener  = newPersistener;
	}

}
  • 因爲方法可以命名,所以setter方法注入在描述性上要比構造方法注入好些。另外,setter方法可以被繼承,允許設置默認值,而且有良好的IDE支持。缺點當然就是對象無法在構造完成後馬上進入就緒狀態

4.IOC模式帶給我們的好處

不只是一個方向上的轉變,這種模式不會對業務對象構成很強的侵入性,使用IOC後,對象具有更好的可重用性,和可擴展性 和可測試性。

總結:IoC是一種可以幫助我們解耦各業務對象間依賴關係的對象綁定方式。爲什麼說能夠解耦呢,因爲從另一個角度看,DI即依賴注入,帶來了鬆耦合,我們知道,如果一個對象只通過接口(而不是具體實現或初始化過程)來表明依賴關係,那麼這種依賴就能夠在對象本身毫不知情的情況下,用不同的具體實現進行替換。

擴展說明:緊密耦合的代碼難以測試,難以複用,難以理解,另一方面,一定程度的耦合又是必須的,完全沒有耦合的代碼什麼也做不了,爲了完成有實際意義的功能,不同的類必須以適當的方式進行交互。總而言之,耦合是必須的,但應當被謹慎地管理

5.掌管大局的IoC Service Provider

雖然業務對象可以通過IoC方式聲明相應的依賴,但是最終仍然需要通過某種角色或者服務將這些相互依賴的對象綁定到一起,而IoC Service Provider就對應IoC場景中的這一角色。

IoC Service Provider在這裏是一個抽象出來的概念,它可以指代任何將IoC場景中的業務對象綁定到一起的實現方式,它可以是一段代碼,也可以是一組相關的類,甚至可以是比較通用的IoC框架或者IoC容器實現。
比如:

IFXNewsListener  newListener  =  new DowJonesNewsListener();	//①
IFXNewsPersister  newsPersister  =  new DowJonesNewsPersister();//②	
FXNewsProvider  newsProvider  =  new FXNewsProvider(newListener  , newsPersister );//③	

①和②就是下面將要說的職責之一:業務對象的構建管理
③ 就是業務對象間的依賴綁定

5.1 職責

  • 業務對象的構建管理:
    在IoC場景中,業務對象無需關心所依賴的對象如何構建如何獲取,但這部分工作始終需要有人來做。所以, Ioc
    Service Provider需要將對象的構建邏輯從客戶端對象那裏剝離出來,以免這部分邏輯污染業務對象的實現
  • 業務對象間的依賴綁定:
    對於Ioc Service Provider來說,這個職責是最艱鉅也是最重要的,這是它的最終使命之所在。如果不能完成這個職責,那麼,無論業務對象如何的“呼喊”,也不會得到依賴對象的任何響應(最常見的倒是會收到一個NullpointerException)。Ioc Service Provider通過結合之前構建和管理的所有業務對象,以及各個業務對象間可以識別的依賴關係,將這些對象所依賴的對象注入綁定,從而保證每個業務對象在使用的時候,可以處於就緒狀態。

5.1.1由上面的職責,我們可以得知IoC容器在我們的具體使用場景中到底幫助了我們做了些什麼
以下例子來自https://www.zhihu.com/question/23277575/answer/169698662
假設有以下四個類,它們的關係如下圖所示。
當我們需要創建一輛汽車時,我們需要編寫如下代碼
在這裏插入圖片描述

Tire tire = new Tire();//輪胎
Bottom bottom = new Bottom(tire);//底盤
Framework framework = new Framework(bottom);//車身
Car car = new Car(framework);//汽車

當有了IoC容器之後,對車類進行初始化的那段代碼發生的地方,轉移到了IoC容器裏。
在這裏插入圖片描述

這有什麼好處呢?
IoC 容器的第一個好處是:這個容器可以自動對你的代碼進行初始化,你只需要維護一個容器(可以是xml可以是一段代碼),而不用每次初始化一輛車都要親手去寫那一大段初始化的代碼。
IoC 容器的第二個好處是:我們在創建實例的時候不需要了解其中的細節。在上面的例子中,我們自己手動創建一個車instance時候,是從底層往上層new的:

在這裏插入圖片描述

這個過程中,我們需要了解整個Car/Framework/Bottom/Tire類構造函數是怎麼定義的,才能一步一步new/注入。

而IoC Container在進行這個工作的時候是反過來的,它先從最上層開始往下找依賴關係,到達最底層之後再往上一步一步new(有點像深度優先遍歷):
在這裏插入圖片描述
這裏IoC Container可以直接隱藏具體的創建實例的細節,在我們來看它就像一個工廠:
在這裏插入圖片描述

我們就像是工廠的客戶。我們只需要向工廠請求一個Car實例,然後它就給我們按照Config創建了一個Car實例。我們完全不用管這個Car實例是怎麼一步一步被創建出來。

實際項目中,有的Service Class可能是十年前寫的,有幾百個類作爲它的底層。假設我們新寫的一個API需要實例化這個Service,我們總不可能回頭去搞清楚這幾百個類的構造函數吧?IoC Container的這個特性就很完美的解決了這類問題——因爲這個架構要求你在寫class的時候需要寫相應的Config文件,所以你要初始化很久以前的Service類的時候,前人都已經寫好了Config文件,你直接在需要用的地方注入這個Service就可以了。這大大增加了項目的可維護性且降低了開發難度。

5.2 關係管理方式

對於IoC Service Provider 來說,它也同樣需要知道自己所管理和掌握的被注入對象和依賴對象之間的對應關係。有以下幾種方式

5.2.1 直接編碼方式:

在容器啓動之前,我們就可以通過程序編碼的方式將被注入對象和依賴對象註冊到容器中,並明確它們相互之間的依賴關係

IoContainer container  =  …;//獲取容器
Container.register(FXNewsProvider.class,new  FXNewsProvider());//往容器中註冊對象
Container.register(IFXNewsListener.class, new DowJonesNewsListener());//往容器中註冊對象
…
FXNewsProvider newsProvider  =  (FXNewsProvider) container.get(FXNewsProvider.class);
//從容器中獲取對象
newProvider.getAndPersistNews();//調用獲取到的對象的方法

5.2.2 配置文件方式:

配置文件類別有 普通文本文件,properties文件,XML文件等,最爲常見的還是通過XML文件來管理和保存依賴注入的信息。

5.2.3 元數據方式:略

6.好了,以上IoC的一些介紹內容就是我經過查找資料以及我的個人見解得出的總結,歡迎各位進行評論,一起學習

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