維基百科中說「依賴注入是一種軟件設計模式,在這種模式中,一種或多種依賴項(或服務)被注入,或者經過引用傳遞到一個依賴對象(或客戶端),併成爲客戶端狀態的一部分,這種模式將客戶端依賴的建立和其自身行爲分離開來,容許程序設計鬆散耦合,
遵循依賴注入和單一責任原則。它直接對比了服務定位模式,該模式容許客戶瞭解其用來查找依賴項的系統」。git
在不使用依賴注入技術的狀況下,很難去管理依賴項和去開發一個模塊化而且結構良好的應用程序。github
在應用程序中,類與類之間是互相依賴,假設咱們如今有一個使用倉儲庫將實體插入數據庫的應用程序服務。這種情形下,應用程序服務類依賴於倉儲庫類,看下面的例子:web
public class PersonAppService { private IPersonRepository _personRepository; public PersonAppService() { _personRepository = new PersonRepository(); } public void CreatePerson(string name, int age) { var person = new Person { Name = name, Age = age }; _personRepository.Insert(person); } }
PersonAppService使用PersonRepository往數據庫中插入了Person,雖然看起來也沒什麼害處,可是這段代碼仍是有一些問題:數據庫
爲了克服上述問題,可使用工廠模式,倉儲類的建立時抽象的,以下所示:設計模式
public class PersonAppService { private IPersonRepository _personRepository; public PersonAppService() { _personRepository = PersonRepositoryFactory.Create(); } public void CreatePerson(string name, int age) { var person = new Person { Name = name, Age = age }; _personRepository.Insert(person); } }
PersonRepositoryFactory是一個靜態類,它建立並返回IPersonRepository.這稱爲服務器定位模式。建立問題被解決了,由於PersonAppService 不知道IPersonRepository的建立是怎麼實現的,而且它和PersonRepository 的實現是獨立的。可是仍然有一些問題:安全
PersonRepositoryFactory是一個靜態類,它建立並返回IPersonRepository.這稱爲服務器定位模式。建立問題被解決了,由於PersonAppService 不知道IPersonRepository的建立是怎麼實現的,而且它和PersonRepository 的實現是獨立的。可是仍然有一些問題:服務器
有一些最佳實踐(模式)能夠幫助咱們依賴於其餘類:app
上面的例子能夠重寫以下:框架
public class PersonAppService { private IPersonRepository _personRepository; public PersonAppService(IPersonRepository personRepository) { _personRepository = personRepository; } public void CreatePerson(string name, int age) { var person = new Person { Name = name, Age = age }; _personRepository.Insert(person); } }
這被稱爲構造函數注入。PersonAppService 不知道哪些類實現了IPersonRepository或者它是怎麼被建立的.當PersonAppService 須要的時候,咱們首先建立一個IPersonRepository,並將其傳給PersonAppService的構造函數。模塊化
var repository = new PersonRepository(); var personService = new PersonAppService(repository); personService.CreatePerson("John Doe", 32);
構造函數注入是使類獨立於依賴對象的建立的一種很好的方法,可是上面的代碼有一些問題:
幸運的是,有依賴注入框架,能夠自動管理依賴項。
構造函數注入模式是提供類依賴關係的一種很好的方式。這樣你就不能在不提供依賴項的狀況下建立類的實例,它也是一種明確聲明類的強大方法,這樣就能夠正常工做。
在一些情形下,類可能依賴其餘類,可是能夠沒有它正常工做。好比對於諸如日誌記錄之類的橫切關注點。類能夠在不進行日誌記錄的狀況下工做,可是若是您向它提供一個日誌記錄器,它就能夠寫入日誌。在這種狀況下,您能夠將依賴項定義爲公共屬性,而不是在構造函數中獲取它們。考慮一下咱們將如何在PersonAppService中寫入日誌。咱們能夠這樣重寫這個類:
public class PersonAppService { public ILogger Logger { get; set; } private IPersonRepository _personRepository; public PersonAppService(IPersonRepository personRepository) { _personRepository = personRepository; Logger = NullLogger.Instance; } public void CreatePerson(string name, int age) { Logger.Debug("Inserting a new person to database with name = " + name); var person = new Person { Name = name, Age = age }; _personRepository.Insert(person); Logger.Debug("Successfully inserted!"); } }
NullLogger.Instance是實現了ILogger的單例對象,可是它不作任何事。它不寫日誌。它用空的方法體實現了ILogger 。若是咱們在建立PersonAppService對象以後設置了Logger屬性,那麼PersonAppService 能夠寫日誌:
var personService = new PersonAppService(new PersonRepository()); personService.Logger = new Log4NetLogger(); personService.CreatePerson("John Doe", 32);
假設Log4NetLogger實現了ILogge,而且它使用Log4Net 庫寫日誌,這樣PersonAppService 能夠真正的寫日誌。若是咱們沒有設置Logger ,它不會寫日誌。咱們能夠說ILogger是PersonAppService的一個可選擇的依賴。
大多數依賴注入框架都支持屬性注入模式。
有許多依賴注入框架能夠自動解決依賴項。他們能夠遞歸的建立全部依賴項,以及依賴項的依賴項的對象。
僅僅是簡單的使用構造函數和屬性注入模式編寫類,依賴注入框架會作剩下的事情,在一個好的應用程序中,你的類是獨立的,甚至獨立於依賴注入框架。在整個應用程序中,只有幾行代碼或類顯式地與DI框架交互。
ABP使用 Castle Windsor框架進行依賴注入。它是最成熟的DI框架之一。還有許多其餘框架,例如Unity、Ninject、StructureMap和Autofac。
在一個依賴注入框架,首先將你的接口/類註冊到依賴注入框架,而後解析(建立)一個對象。在Castle Windsor中,像這樣:
var container = new WindsorContainer(); container.Register( Component.For<IPersonRepository>().ImplementedBy<PersonRepository>().LifestyleTransient(), Component.For<IPersonAppService>().ImplementedBy<PersonAppService>().LifestyleTransient() ); var personService = container.Resolve<IPersonAppService>(); personService.CreatePerson("John Doe", 32);
首先,咱們建立了WindsorContainer,而且使用其接口註冊了PersonRepository 和 PersonAppService。咱們使用這個容器窗口了IPersonAppService。它用依賴關係建立了具體的類PersonAppService,而後返回它。在這個簡單的例子中,可能使用DI框架的優點不明顯。然而在一個真正的企業項目中,你可能有多個類和依賴項。依賴項的註冊和對象的使用時分開的,而且在應用程序啓動時只建立一次。
注意,咱們還將對象的生命週期設置爲瞬態的。這意味着不管什麼時候解析這些類型的對象,都會建立一個新實例。有許多不一樣的生命週期,例如單例。
ABP使得對依賴注入框架的使用幾乎是不可見的。它還經過遵循最佳實踐和約定來幫助咱們寫應用程序。
註冊依賴
在ABP有許多方法能夠將類註冊到依賴注入系統,大多數狀況下,傳統的註冊就足夠了。
傳統的註冊
ABP根據約定自動註冊全部的 Repositories, Domain Services, Application Services, MVC Controllers and Web API Controllers,例如,這裏有一個IPersonAppService接口和一個實現它的PersonAppService類:
public interface IPersonAppService : IApplicationService { //... } public class PersonAppService : IPersonAppService { //... }
ABP自動註冊它,由於它實現了IApplicationService接口(它只是一個空接口)。它被註冊爲transient瞬態,也就是說每次使用都會建立它。當咱們將IPersonAppService接口(使用構造函數注入)注入到類中時,將自動建立一個PersonAppService對象並將其傳遞到構造函數中。
這裏的命名約定很是重要。例如,您能夠將PersonAppService的名稱更改成MyPersonAppService或包含「PersonAppService」後綴的其餘名稱。這將它註冊到IPersonAppService,由於它有相同的後綴。可是,若是不能沒有後綴,好比「PeopleService。若是您這樣作,它不會自動註冊到IPersonAppService。相反,它使用自注冊(而不是接口)註冊到DI框架。在這種狀況下,您能夠手動註冊它。
ABP能夠按照約定註冊程序集:
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
Assembly.GetExecutingAssembly() 獲取對包含此代碼的程序集的引用。咱們能夠將其餘程序集傳遞給RegisterAssemblyByConvention方法。這一般是在初始化模塊時完成的。
咱們能夠經過實現IConventionalRegisterer接口,而後調用IocManager.AddConventionalRegisterer方法來編寫本身的常規註冊類。咱們應該將其添加到模塊的預初始化方法中。
輔助接口Helper Interfaces
您可能想註冊一個不符合常規註冊規則的特定類。ABP提供ITransientDependency、IPerWebRequestDependency和ISingletonDependency接口做爲快捷方式。例如:
public interface IPersonManager { //... } public class MyPersonManager : IPersonManager, ISingletonDependency { //... }
經過這種方式,WOMEN 能夠輕鬆註冊MyPersonManager。當須要注入IPersonManager時,將使用MyPersonManager類。注意,依賴項聲明爲單例。建立MyPersonManager的一個實例,並將相同的對象傳遞給全部須要的類。它在第一次使用時實例化,而後在應用程序的整個生命週期中使用。
注意:IPerWebRequestDependency只能在web層中使用。
若是傳統的註冊不能知足您的須要,您可使用IocManager或Castle Windsor來註冊您的類和依賴項
使用IocManager
你可使用IocManager來註冊依賴項(一般在模塊定義類的預初始化方法中):
IocManager.Register<IMyService, MyService>(DependencyLifeStyle.Transient);
使用Castle Windsor API
您可使用IocManager.IocContainer屬性訪問Castle Windsor容器並註冊依賴項。例子:
IocManager.IocContainer.Register(Classes.FromThisAssembly().BasedOn<IMySpecialInterface>().LifestylePerThread().WithServiceSelf());
解決依賴關係
註冊通知IOC(控制反轉)容器(也稱爲DI框架)關於類、它們的依賴關係和生命週期。在應用程序的某個地方,您須要使用IOC容器建立對象。ASPNET提供了一些解決依賴關係的選項。
構造函數和屬性注入
做爲最佳實踐,咱們應該使用構造函數和屬性注入來得到類的依賴項。例子:
public class PersonAppService { public ILogger Logger { get; set; } private IPersonRepository _personRepository; public PersonAppService(IPersonRepository personRepository) { _personRepository = personRepository; Logger = NullLogger.Instance; } public void CreatePerson(string name, int age) { Logger.Debug("Inserting a new person to database with name = " + name); var person = new Person { Name = name, Age = age }; _personRepository.Insert(person); Logger.Debug("Successfully inserted!"); } }
IPersonRepository從構造函數注入,ILogger注入一個公共屬性。這樣,代碼將徹底不知道依賴項注入系統。這是使用DI系統最合適的方式。
IIocResolver、IIocManager和IScopedIocResolver
咱們可能不得不直接解決您的依賴關係,而不是使用構造函數和屬性注入。這應該儘量避免,但也多是不可能的。ABP提供了一些能夠輕鬆注入和使用的服務。例如:
public class MySampleClass : ITransientDependency { private readonly IIocResolver _iocResolver; public MySampleClass(IIocResolver iocResolver) { _iocResolver = iocResolver; } public void DoIt() { //Resolving, using and releasing manually var personService1 = _iocResolver.Resolve<PersonAppService>(); personService1.CreatePerson(new CreatePersonInput { Name = "John", Surname = "Doe" }); _iocResolver.Release(personService1); //Resolving and using in a safe way using (var personService2 = _iocResolver.ResolveAsDisposable<PersonAppService>()) { personService2.Object.CreatePerson(new CreatePersonInput { Name = "John", Surname = "Doe" }); } } }
MySampleClass是應用程序示例類。它被構造器注入IIocResolver,並使用它解析和釋放對象。解析方法有一些重載,能夠根據須要使用。Release方法用於釋放組件(對象)。若是咱們手工解析一個對象,調用Release是很是重要的。不然,應用程序可能有內存泄漏。爲了確保釋放對象,儘量使用ResolveAsDisposable(如上面的示例所示)。Release在using塊的末尾自動調用。
IIocResolver(和IIocManager)還具備CreateScope擴展方法(在app. dependency名稱空間中定義)來安全地釋放全部已解析的依賴項。
using (var scope = _iocResolver.CreateScope()) { var simpleObj1 = scope.Resolve<SimpleService1>(); var simpleObj2 = scope.Resolve<SimpleService2>(); //... }
在using塊結束處,全部已解析的依賴項將自動刪除。還可使用IScopedIocResolver注入scope。咱們能夠注入這個接口並解決依賴關係。當釋放類時,全部已解析的依賴項都將被釋放。使用這個仔細!若是咱們的類有很長的生命週期(假設它是一個單例),而且咱們解析了太多的對象,那麼全部的對象都將保留在內存中,直到咱們的類被釋放。
若是咱們想直接訪問IOC容器(Castle Windsor)來解決依賴關係,咱們能夠構造-注入IIocManager並使用IIocManager.IocContainer屬性。若是咱們在靜態上下文中或者不能注入IIocManager,做爲最後的手段,可使用單例對象IocManager。實例無處不在。然而,在這種狀況下,代碼並不容易測試。
擴展知識
IShouldInitialize接口
有些類在第一次使用以前須要初始化。IShouldInitialize有一個Initialize()方法。若是實現了它,那麼在建立對象以後(在使用對象以前)會自動調用Initialize()方法。須要注入/解析對象才能使用該特性。
ASP.NET MVC和ASP.NET Web API集成
咱們必須調用依賴注入系統來解析依賴關係圖中的根對象。在ASPNETMVC應用程序中,它一般是一個控制器類。咱們還能夠在控制器中使用構造器和屬性注入模式。當請求到達應用程序時,使用IOC容器建立控制器,並遞歸地解析全部依賴項。這是怎麼發生的?這一切都是由ABP經過擴展ASP.NET MVC的默認控制器工廠自動完成的。對於ASP.NETWeb API也是如此。咱們沒必要擔憂建立和處理對象。
ASPNETCore已經有一個內置的依賴注入系統,帶有Microsoft.Extensions.DependencyInjection包.ABP使用Castle.Windsor.MsDependencyInjection包將其依賴注入系統集成到ASP.Net Core.、
最後:
只要遵循規則並使用上面的結構,ABP就能夠簡化和自動化依賴項注入。大多數時候不須要咱們作什麼。若是須要,能夠直接使用Castle Windsor的原始功能執行許多任務,好比定製註冊、注入掛鉤、攔截器等等。