Spring貫穿表現層、業務層、持久層。但Spring並不是想取代已有的框架,而是以高度的開放性與他們無縫結合。
Spring的核心容器就是一個超級大工廠,所有的Java對象都被當成Spring容器管理的對象——Spring把容器中的一切對象統稱爲Bean。
Spring使用XML配置文件來管理容器中的Bean,只要是一個Java類並被配置在XML文件中Spring就可以管理他。
加入A組件調用了B組件的方法,我們就可以稱A組件依賴於B組件。我們通過使用依賴注入,Java EE應用中的各種組件不需要以硬編碼方式耦合在一起,甚至無需使用工廠模式。當某個Java 實例需要其他Java 實例時,系統自動提供所需要的實例,無需程序顯示獲取,這種自動提供java實例我們謂之爲依賴注入,也可以稱之爲控制反轉(Inversion of Control IoC)。
其實不管是控制反轉還是依賴注入,他們都可以這樣理解:當某個Java實例(調用者)需要另一個Java實例(被調用者)時,在傳統的程序設計過程中,通常有調用者來創建被調用者的實例。但是在依賴注入/控制反轉模式下,創建被調用者的工作不再是有調用者來完成,而是由spring容器來完成,然後注入調用者。
對於Spring而言,Spring採用動態、靈活的方式來管理各種對象。對象與對象之間的具體實現都是透明的。Spring的依賴注入對調用者和被調用者幾乎沒有任何要求,完全支持對POJO之間依賴關係的管理。
依賴注入通常有如下兩種:
1、 設置注入:IoC容器使用屬性的setter方法來注入被依賴的實例。
2、 構造注入:IoC容器使用構造器來注入被依賴的實例。
一、設值注入
設值注入是指IoC容器使用屬性的setter方法來注入被依賴的實例。這種注入方式比較簡單、直觀。
下面是Person接口,該接口定義了一個Person規範。使用Spring時無論Dao組件、業務邏輯組件,最好都要先定義其接口,然後再定義實現他的類。(面向接口編程)
Axe接口:
Person的實現類。
上面的代碼實現了Person接口的userAxe()方法,實現該方法時調用了axe的的chop()方法,這就是典型的依賴關係。
在上面的Chinese類中,Chinese類並不知道它要調用的axe實例在哪裏,也不知道axe實例是如何實現的,它只是需要調用一個axe實例,這個Axe實例將由Spring容器負責注入。
Axe的實現類:StoneAxe類
直到這裏,程序依然不知道Chinese類和Axe實例耦合,Spring也不知道!實際上,Spring需要使用XML配置文件來指定實例之間的依賴關係。
Spring採用了XML文件作爲配置文件。
對於本應用的XML配置文件如下:
在配置文件中,Spring配置Bean實例通常會指定兩個屬性:
id:指定該Bean的唯一標識,程序會通過id屬性值來訪問該Bean實例。
class:指定該Bean的實現類,此處不可再用接口,必須是實現類,Spring容器會使用XML解析器讀取該屬性值,並利用反射來創建該實現類的實例。
Spring通過反射機制根據<bean…/>元素穿件一個Java對象,並以<bean…/>中的id屬性值爲其key。Spring根據<property …/>就會以反射機制調用一次setter()方法。 簡單講就是:bean…/>驅動Spring調用構造函數創建對象,<property …/>驅動Spring調用setter()方法,兩者的執行是先後關係,基本沒有間隔。
每個Bean的id屬性是該Bean的唯一標識,程序通過id屬性訪問Bean,Bean與Bean的依賴關係也是通過id屬性關聯。
測試程序:
執行上面的程序,執行結果如下:
主程序調用Person的userAxe()方法時,該方法的方法體內需要使用Axe實例,但程序沒有任何地方將特定的Person實例和Axe實例耦合在一起,也就是說程序沒有爲Person實例傳入Axe實例,Axe實例有Spring在運行期間注入。
Spring IoC容器有如下3個基本要點:
1、 應用程序的各個組件面向接口編程。面向接口編程可以將各個組件的耦合提升到接口層次,從而有利於項目後期的擴展。
2、 應用程序的各組件不再由程序主動產生,而是由Spring容器來負責產生,並初始化。
3、 Spring採用配置文件、或者Annotation來管理Bean的實現類、依賴關係,Spring容器則根據配置文件,利用反射機制來創建時間,併爲之注入依賴關係。
--------------------------------------------------目前只用到過前一種注入方式----------------------------------------------------------
二、構造注入
構造注入就是利用構造器來設置依賴關係的方式。
Japanese類:
上面的Chinese類並沒有setter方法,僅僅只是提供了一個帶Axe屬性的構造器,Spring將通過該構造器爲Chinese注入所依賴的Bean實例。
構造注入的配置文件需要做一些修改。爲了使用構造注入,使用<constructor-arg…/>元素來指定構造器的參數。如下
上面的配置文件使用<contructor-arg…/>元素指定了一個構造器參數,該參數類型是Axe,這指定Spring調用Chinese類裏帶一個Axe參數的構造器來創建chinese實例,因爲使用了有參數的構造器創建實例,所以當Bean實例被創建完成後,該Bean的依賴關係也就已經設置完成。
他的執行效果與設值注入的執行效果一樣。但是還是有點卻別:創建Person實例中Axe的屬性時機不同—設值注入式先通過無參數的構造器創建一個Bean實例,然後調用它的setter方法注入依賴關係,而構造注入則是直接調用有參數的構造器,當Bean實例創建完成後,依賴關係也已經完成。
三、兩種注入方式的對比
Spring支持兩種依賴注入方式,這兩種依賴注入方式並沒有好壞之分,只是適合的場景有所不同。
設值注入有如下優點:
1、 與傳統的JavaBean的寫法更相似,程序開發人員更加容易理解,接受。通過setter方法設定依賴關係顯得更加直觀、自然。
2、 對於複雜的依賴關係,如果採用構造注入,會導致構造器過於臃腫,難以閱讀。Spring在創建Bean實例時,需要同時實例化其依賴的全部實例,因此導致性能下降。而設值注入,則可以避免這些問題。
3、 尤其是在某些屬性可選的情況下,多參數的構造器更加笨重。
但是構造器也有如下優勢:
1、 構造注入可以再構造器中決定依賴關係的注入順序,優先依賴的優先注入。
2、 對於依賴關係無須變化的Bean,構造注入更有用處。因爲沒有setter方法,所有的依賴關係全部在構造器中設定,因此,無須擔心後續的代碼對依賴關係產生破壞。
3、 依賴關係只能在構造器中設定,則只有組件的創建者才能改變組件的依賴關係。對組件的調用者而言,組件內部的依賴關係完全透明,更加符合高內聚的原則。
通過上面的對比。所以建議用以設值注入爲主,構造注入爲輔的注入策略。對於依賴關係無須變化的注入,儘量採用構造注入;而其他的依賴關係,則考慮設值注入。