Unity 是一個依賴注入容器,自然依賴注入是它最重要的功能。從以往的討論來看,矛盾都集中於 Unity 侵入了依賴的對象,這可能是 Unity 在預覽版的文檔中沒有說明,加上某個外國牛人的某篇文章造成的惡果。因為,Unity 同樣可以通過配置文件支持非侵入式的依賴注入。本文將從使用 Attribute 和配置文件兩個方面對 Unity 的依賴注入的支持進行闡述。
Unity 支持構造函數、屬性和方法調用注入。
構造函數注入
構造函數注入包含了二種情況,一種是類僅有一個構造函數時,Unity 可以進行自動注入;另一種情況是,類包含多個構造函數時,必須使用 Attribute 或者配置文件指定注入時使用的構造函數。
假設現在有一個 Telephone 類,僅包含了一個帶有 IDialer 類型的參數的構造函數。
如下所示:
那麼,我們可以這樣來使用:
此時,我們不需要對 Telephone 進行任何配置。
然後,再添加一個帶有 string 類型參數的構造函數(注意,此構造函數沒有實質用處),如下所示:
如果此時我們再使用上面示例中的代碼,系統將會拋出 ResolutionFailedException 異常,因為此時 Unity 無法解析要使用哪個構造函數來生成對象,但是,如果其中有一個構造函數為默認構造函數時,Unity 會自動選擇帶參數的那一個構造函數。
此時,我們就需要為 Unity 指定將被使用的構造函數,方法是在將被使用的構造函數上加上 InjectionConstructor 特性。如下所示:
這樣,Unity 又能正確的選擇構造函數並創建新的對象了。
下面,再來看看使用配置文件來送到同樣的效果。先在項目中添加一個應用程序配置文件,然後加入如下代碼:
從上面中我們可以看到,依賴注入的配置是通過 typeConfig 元素來指定的,其子元素 constructor 指定了構造函數依賴注入,並且用元素 param 指出,將使用參數類型為 IDialer 的構造函數。另外,dependency 元素指定此參數的的值使用依賴注入,這樣,Unity 就自動根據容器中注冊的類型映射為構造函數來創建一個對象實例。Dependency 元素可以用 value 元素來代替以直接指定一個常量。
然後,需要將客戶代碼修改如下,以應用配置:
運行代碼,我們將可以得到同樣的結果。
屬性注入(setter)
屬性注入用於為目標對象的特定屬性自動創建指定類型的值。
在前面的 Telephone 類中,有一個名為 Dialer 的 Dialer 類型的屬性,Dialer 類是一個抽象類。Telephone 的 Dialer 屬性如下圖所示:
然後,我們用如下代碼來使用它。(注意,代碼中分別注冊了二個類型映射,Dialer 和 IDialer,Telephone 的構造函數的參數為 IDailer 類型,而Dialer 屬性的類型為 Dialer 抽象類):
運行上面的代碼,我們可以得到下面的結果:
可能你已經發現,這並不是我們想要的結果,Dialer 類映射的類型應該是 ButtonTypeDialer 類,而不是結果中的 FigurePlateDialer 類型,也就是說,Unity 僅僅應用上了構造函數依賴注入,而沒有應用屬性注入,為什麼呢?因為屬性注入必須用特性或者配置文件指定,Unity 才會應用。
我們為 Telephone 的 Dialer 屬性加上 Dependency 特性,如下所示:
再次運行上面的代碼,我們就能看到輸出的是 ButtonTypeDialer 了。注意,在這裡,Unity 同時應用了構造函數注入和屬性注入,為什麼我們看到的結果是屬性注入的結果呢?因為構造函數注入是在屬性注入之前被應用的,如果沒有創建對象,屬性注入不能應用到不存在的對象的屬性上。
和構造函數注入一樣,我們可以用配置文件來指定屬性注入。在這可以通過在配置文件中的 typeConfig 元素中添加 property 元素來實現,如下所示:
注意,在此類型的指定使用的是 propertyType ,而不是構造函數中所使用的 parameterType。如果使用默認類型映射的話,dependency 元素也可以忽略,如果需要指定命名映射,則可以用 dependency 的 name 屬性來指定。同時,dependency 元素也可以用 value 元素來代替。
運行如下代碼,我們就可以得到期望的結果了:
方法調用注入
方法調用注入用於指示 Unity 在創建對象後,必須調用對象指定的方法,以執行指定的初始化等操作。
方法調用注入相對比較簡單,如果使用 Attribute 的方式的話,就可以用 InjectionMethod 特性來標識方法,例如:
與之對應的配置文件如下所示:
是不是看起來很眼熟,對了,它其實與構造函數注入的配置差不多,唯一不同在於方法調用注入需要指定被調用的方法名稱。
小結
Unity 同時支持構造函數注入、屬性注入和方法調用注入,可以使用 Attribute 特性以侵入服務類的方式也可以使用配置文件以非侵入的方式來使用。
本文配套源碼