深入剖析Spring(一)——IoC的基本概念(從面向對象角度介紹)

IoC與DI

IoC和DI是Spring的兩個核心概念,很多人都把它們視爲相同的東西,但事實並非如此。 
IoC(Inversion of Control):控制反轉。 
DI(Dependency Injection):依賴注入。

爲了方便理解,先給出結論:

控制反轉是目的,依賴注入是實現控制反轉的手段。

控制反轉是一種面向對象的思想,它是一種寬泛的概念,只要一個類將對它內部狀態的控制權交由其他機制去完成即爲『控制反轉』。控制反轉是爲了降低類與類之間的耦合度。

而Spring採用依賴注入這一具體的手段來達到控制反轉的目的。

依賴注入詳解 
一個類內部往往有很多成員變量,如:

class A {
    private Person chaimm;
}
  • 1
  • 2
  • 3

上述代碼在面向對象中可以描述爲:

  • A類和Person類之間存在依賴關係;
  • A依賴於Person;
  • A爲依賴類;
  • Perosn爲被依賴類;

通常情況下,依賴類需要自己去創建並維護被依賴類的對象,如:

class A {
    private Person chaimm = new Person();
}
  • 1
  • 2
  • 3

但依賴注入的做法是:將被依賴對象的創建與維護工作交由專門的機構,而依賴類中只需要聲明所需要的成員變量。 
也就是說,依賴類原本需要主動去獲取對象,但採用依賴注入後對象由第三方機構提供,自己僅需聲明需要什麼對象即可。 
這樣做的目的就是爲了降低兩個類之間的耦合程度。 
PS:在Spring中,那個創建、管理對象的機構就稱爲『IoC Service Provider』。

但此時還沒體現出依賴注入能降低耦合度這一點,只有當依賴注入與面向接口編程結合起來,才能真正發揮依賴注入的優勢。接下來先介紹一下『面向接口編程』。

什麼是面向接口編程? 
一個類依賴其他類的目的是爲了獲取其他類所提供的服務,可能這種服務有多種實現,我們可能需要根據不同的場景使用不同的實現。此時,我們可以使用多態,將同一功能的多種實現抽象出一個接口,併爲所有實現定義一套相同的API。在使用時聲明接口類型的變量而非實現類的變量,並將實現類的對象賦給接口變量,最後用接口變量去調用實現類的服務,如:

class A {
    private Super super = new SuperImpl_1();

    public static void main ( String[] args ) {
        // 使用Super提供的服務
        super.method_1();
        super.method_2();
        super.method_3();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

這樣,當想使用SuperImpl_2提供的功能時,只需替換Super的實現類,其他地方不做任何變化:

private Super super = new SuperImpl_2();
  • 1

上述過程就是面向接口編程的思想:若某一類服務有多種不同的實現,我們需要抽象出一個接口,並在接口中定義一套API。在使用時聲明接口類型變量,並用實現類的對象賦值。接下來通過接口類型的變量調用服務即可。當功能發生變化時,僅需替換實現類即可。

在面向接口編程的基礎上使用依賴注入的好處 
上述過程如果要換一種實現,就必須要修改A類的代碼,再重新編譯。而使用了依賴注入後,由於依賴類不需要自己創建維護被依賴對象,該過程由IoC Service Provider完成。因此,當需要替換實現類時,只需在IoC Service Provider中修改,被依賴類、依賴類都不會受到影響,此時這兩個類是松耦合的。

依賴注入的三種方式

下面介紹三種方式,將被依賴對象注入給依賴類。

1. 構造器注入

將被依賴對象通過構造函數的參數注入給依賴對象,並且在初始化對象的時候注入。

優點: 
對象初始化完成後便可獲得可使用的對象。

缺點: 
1. 當需要注入的對象很多時,構造器參數列表將會很長; 
2. 不夠靈活。若有多種注入方式,每種方式只需注入指定幾個依賴,那麼就需要提供多個重載的構造函數,麻煩。

2. setter方法注入

IoC Service Provider通過調用成員變量提供的setter函數將被依賴對象注入給依賴類。

優點: 
靈活。可以選擇性地注入需要的對象。

缺點: 
依賴對象初始化完成後由於尚未注入被依賴對象,因此還不能使用。

3. 接口注入

依賴類必須要實現指定的接口,然後實現該接口中的一個函數,該函數就是用於依賴注入。該函數的參數就是要注入的對象。 
接口注入中,接口的名字、函數的名字都不重要,只要保證函數的參數是要注入的對象類型即可。

缺點: 
侵入行太強,不建議使用。

PS:什麼是侵入行? 
如果類A要使用別人提供的一個功能,若爲了使用這功能,需要在自己的類中增加額外的代碼,這就是侵入性。

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