依賴注入html
若是你已經知道依賴注入,構造函數和屬性注入模式,能夠直接跳到下一部分。github
維基百科說:「依賴注入是一種軟件設計模式,一個或多個依賴項(或服務)被注入或經過引用傳遞到一個依賴對象,而且成爲客戶端狀態的一部分。這種模式把客戶端依賴項的建立從它本身的行爲中分離出來,容許程序設計成鬆耦合的,遵循依賴倒置和單一職責的原則。和服務定位器模式相比,它容許客戶端知道他們使用的系統查找依賴項。」web
不使用依賴注入技術,很難管理依賴項和發佈一個結構良好的應用。數據庫
在一個應用裏,類之間相互依賴。假設咱們有一個使用倉儲插入實體到數據的應用服務,在這種狀況下,這個應用服務類依賴倉儲類。以下示例:api
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實體。這段代碼有如下問題:安全
可使用工廠模式克服這些問題中的一部分。所以,倉庫類的建立應該是抽象的。請看以下代碼:mvc
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的實現。可是,仍然有一些問題:框架
有一些依賴其餘類的最佳實踐(模式)。
上面的實例能夠按以下所示的重寫:
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("Yunus Emre", 19);
構造函數注入是使類獨立於依賴對象建立的完美方法。可是,上面的代碼有一些問題:
幸運的是,有依賴注入框架自動管理依賴項。
構造函數注入模式是提供類依賴項的完美方法。用這種方式,不提供依賴項就不能建立類的實例。也使用強方式顯示的聲明類工做所須要的條件。
可是,在一些狀況下,依賴於另外一個類的類沒有它也能夠工做。這一般適用於橫切關注點如日誌。類能夠沒有日誌照樣工做,可是若是提供一個記錄器它就能夠寫日誌。在這種狀況下,能夠把依賴項定義成公共屬性而不是在構造函數中獲取他們。想象咱們想在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對象後設置記錄器,它就能夠寫日誌了:
var personService = new PersonAppService(new PersonRepository()); personService.Logger = new Log4NetLogger(); personService.CreatePerson("Yunus Emre", 19);
假定Log4NetLogger實現了ILogger,且使用Log4Net類庫寫日誌。從而,PersonAppService實際上能夠寫日誌了。若是咱們沒有設置記錄器,它就不寫日誌。因此,咱們能夠說ILogger是PersonAppService一個可選的依賴項。
幾乎全部的依賴注入框架都支持屬性注入模式。
有許多能夠自動解析依賴項的依賴注入框架。他們能夠建立對象以及其全部依賴項(能夠遞歸依賴的依賴)。因此,只要使用構造函數、屬性注入模式寫類,DI框架會處理剩下的工做!在一個好的應用裏,類甚至獨立於DI框架。在整個應用裏,只有幾行或幾個類顯示的與DI框架交互。
ABP使用Castle Windsor做爲依賴注入框架。它是最成熟的框架之一。還有許多其餘的框架,如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("Yunus Emre", 19);
咱們首先建立WindsorContainer。而後使用接口註冊PersonRepository和PersonAppService。咱們讓容器建立一個IPersonAppService。它建立PersonAppService及其依賴項並返回。在這個簡單例子裏,使用DI框架的優點並非很明顯,可是設想一下在一個真實的企業應用裏將會有許多的類和依賴項。固然,註冊依賴項只在應用啓動的時候註冊一次,建立和使用對象可能會在其餘的地方。
注意,咱們把對象的生命週期生命爲短暫的。意味着不管何時咱們須要類型的一個對象時,一個新的實例將會被建立。還有許多其餘不一樣的生命週期(如單例)。
當你遵循最佳實踐和一些約定編寫應用的時候,ABP幾乎使依賴注入框架的使用無感知的。
在ABP中有不一樣的方式註冊類到依賴注入系統。大多數時候,常規的註冊就足夠了。
ABP默認會自動註冊全部的倉儲、領域服務、應用服務、MVC控制器和Web API控制器。例如,你有一個IPersonAppService接口和一個它的實現類PersonAppService:
public interface IPersonAppService : IApplicationService { //...
} public class PersonAppService : IPersonAppService { //...
}
由於它實現了IApplicationService(是一個空接口),因此ABP會自動註冊它。它會被註冊爲暫時的(每次使用時建立實例)。當你注入(使用構造函數注入)IPersonAppService接口到一個類的時候,PersonAppService對象會被建立而且自動傳遞給構造函數。
命名約定在這裏是很是重要的。例如,你能夠把PersonAppService的名稱改成MyPersonAppService或者其餘包含'PersonAppService'後綴的名字,由於IPersonAppService有這個後綴。可是不能夠把服務命名爲PeopleService。若是這樣作了,就不會自動註冊IPersonAppService了(它使用自注冊方式註冊到DI框架,不使用接口),因此,若是想要註冊的話能夠手動註冊。
ABP能夠根據約定註冊程序集。可讓ABP根據約定註冊程序集。這是至關簡單的:
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
Assembly.GetExeutingAssembly()獲取包含這些代碼的程序集引用。你能夠傳遞其餘程序集到RegisterAssemblyByConvention方法。這些一般在模塊初始化的時候執行。可參見ABP模塊系統瞭解更多。
能夠編寫自定義的約定註冊類,須要實現IConventionalRegisterer接口,而且在類裏調用IocManager.AddConventionalRegiisterer方法。須要添加到模塊的preinitialize方法中。
你可能想註冊一個特定的類,但這個類不符合約定註冊規則。ABP提供了ITransientDependency和ISingletonDependency接口做爲捷徑。例如:
public interface IPersonManager { //...
} public class MyPersonManager : IPersonManager, ISingletonDependency { //...
}
這樣,就能夠很容易註冊MyPersonManager。當須要注入IPersonManager時,會使用MyPersonManager類。注意,依賴聲明爲單例的。所以,MyPersonManager建立爲單例的,全部須要的類都會傳入相同的對象。只有在首次使用的時候建立,在應用的整個生命週期都會使用相同的實例。
若是約定註冊不能知足的話,一顆使用IocManager或Castle Windsor註冊類和依賴項。
可使用IocManager註冊依賴項(一般在模塊定義類的PreInitialize方法中):
IocManager.Register<IMyService, MyService>(DependencyLifeStyle.Transient);
使用Castle Windsor API
可使用IIocManager.IocContainer屬性訪問Castle Windsor容器和註冊依賴項。例如:
IocManager.IocContainer.Register(Classes.FromThisAssembly().BasedOn<IMySpecialInterface>().LifestylePerThread().WithServiceSelf());
瞭解更多信息,參見Windsor文檔。
註冊會通知IOC(Inversion of control)容器(a.k.a DI框架)知道有哪些類、依賴項和生命週期。在應用的一些地方,須要使用IOC容器建立對象。ABP提供了一些解析依賴的選擇。
可使用構造函數和屬性注入得到類的依賴項做爲最佳實踐。應該在任何可能的地方這樣作。例如:
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 = "Yunus", Surname = "Emre" }); _iocResolver.Release(personService1); //Resolving and using in a safe way
using (var personService2 = _iocResolver.ResolveAsDisposable<PersonAppService>()) { personService2.Object.CreatePerson(new CreatePersonInput { Name = "Yunus", Surname = "Emre" }); } } }
MySampleClass是個示例類。它經過構造函數注入IIcResolver而且使用它解析和釋放對象。有幾個Resolve的重載方法能夠在須要的時候使用。Release方法用來釋放組件(對象)。若是手動解析了一個對象,那麼調用Release方法是很關鍵的。要否則,應用可能忽悠內存泄露的問題。爲了確保釋放對象,在任何可能的地方使用ResolveAsDisposable(如上例所示)。它會在using塊的結束爲止自動調用Release。
IIocResolver(和IIocManager)有CreateScope擴展方法(定義在Abp.Dependency命名空間),用來安全釋放全部解析的依賴對象。例如:
using (var scope = _iocResolver.CreateScope()) { var simpleObj1 = scope.Resolve<SimpleService1>(); var simpleObj2 = scope.Resolve<SimpleService2>(); //...
}
在using塊的末尾,全部解析的依賴對象自動移除。使用IScopedIocResolver,做用範圍也是能夠注入的。能夠注入這個接口用來解析全部依賴項。當類被釋放的時候,全部解析的依賴項也會被釋放。可是,須要當心使用;例如,若是有個類有很長的生命(比方是單例)而且解析了大量的對象,全部的這些類都會存在內存中,直到這個類被釋放。
若是想直接使用IOC容器(Castle Windsor)解析依賴項,可使用構造函數注入IIocManager並使用IIocManager.IocContainer屬性。若是在一個靜態上下文或者不能注入IIocManager,做爲最後的選擇,可使用單例對象IocManager.Instance。可是,這樣的話代碼就不易測試了。
一些類須要在首次使用的時候初始化。IShouldInitialize有一個Initialize()方法。若是實現了它,當建立對象後(在使用前)Initialize()方法會自動調用。固然,能夠注入或解析這個對象以便使用這個特徵。
咱們必須調用依賴注入系統來解決在依賴圖中的根對象。在ASP.NET MVC應用裏,它一般爲一個控制器類。咱們能夠在控制器裏使用構造函數和屬性注入模式。當請求到達應用時,控制器使用IOC容器建立,全部的依賴遞歸解析。因此,誰作這些呢?這些由ABP經過擴展ASP.NET MVC的默認控制器工廠自動完成。一樣,ASP.NET Web API也是這樣的。不用關心建立和釋放對象。
ASP.NET Core 已經有一個內置的依賴注入系統,在Microsoft.Extensions.DependencyInjection包裏。ABP使用Castle.Windsor.MsDependencyInjection包集成和ASP.NET Core的依賴注入系統。因此,不須要關心它。
ABP簡化且自動使用依賴入住,只要遵循規則並使用上述的結構。大多數時候不須要更多的依賴注入。可是,若是須要,能夠直接使用Castle Windsor的力量執行任何任務(如自定義註冊,注入鉤子,攔截等等)。