返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET樣板項目)”的簡稱。 ASP.NET Boilerplate是一個用最佳實踐和流行技術開發現代WEB應用程式的新起點,它旨在成為一個通用的WEB應用程式框架和項目模板。 ABP的官方網站:http:// ...
ABP是“ASP.NET Boilerplate Project (ASP.NET樣板項目)”的簡稱。
ASP.NET Boilerplate是一個用最佳實踐和流行技術開發現代WEB應用程式的新起點,它旨在成為一個通用的WEB應用程式框架和項目模板。
ABP的官方網站:http://www.aspnetboilerplate.com
ABP官方文檔:http://www.aspnetboilerplate.com/Pages/Documents
Github上的開源項目:https://github.com/aspnetboilerplate
一、依賴註入概念
控制反轉(Inversion of Control,英文縮寫為IoC)是一個重要的面向對象編程的法則來削減電腦程式的耦合問題,也是輕量級的Spring框架的核心。 控制反轉一般分為兩種類型,依賴註入(Dependency Injection,簡稱DI)和依賴查找(Dependency Lookup)。依賴註入應用比較廣泛。
依賴註入是一種軟體設計模式的一個或多個依賴項註入(或服務),或通過引用傳遞,為依賴對象(或客戶)和客戶端狀態的一部分。模式之間建立一個客戶的依賴關係的行為,它允許程式設計是鬆散耦合的,依賴倒置和單一職責原則。它直接對比service locator模式,它允許客戶瞭解他們所使用的系統找到依賴。
依賴註入不是目的,它是一系列工具和手段,最終的目的是幫助我們開發出鬆散耦合、可維護、可測試的代碼和程式。這條原則的做法是大家熟知的面向介面,或者說是面向抽象編程。
理想的軟體開發設計是“高內聚,低耦合”,高內聚側重面向對象編程,低耦合側重面向介面編程,控制反轉、依賴註入、依賴倒置都蘊含著面向介面編程的思想。
常用依賴註入框架:
Unity:微軟patterns&practicest團隊開發的IOC依賴註入框架,支持AOP橫切關註點。
MEF(Managed Extensibility Framework):是一個用來擴展.NET應用程式的框架,可開發插件系統。
Spring.NET:依賴註入、面向方面編程(AOP)、數據訪問抽象,、以及ASP.NET集成。
Autofac:最流行的依賴註入和IOC框架,輕量且高性能,對項目代碼幾乎無任何侵入性。
PostSharp:實現靜態AOP橫切關註點,使用簡單,功能強大,對目標攔截的方法無需任何改動。
Castle Windsor、StructureMap、Ninject
其實我感覺Autofac挺好用的,一直用的Autofac,不知道到Castle Windsor怎麼樣。
二、三層和DDD分層依賴關係
1、三層分層依賴如下圖:
從引用關係我們就能知道各層的依賴關係:BLL需要依賴DAL,因為BLL中用到了DAL層的實體。UI這一層需要依賴BLL,還需要依賴DAL,因為在UI中也用到了DAL層實體。
如果從換個資料庫,DAL需要修改,那DAL的依賴也需要修改。
2、DDD分層依賴關係圖
從上圖可以知道,表現層和數據訪問層都依賴領域模型層,這樣的話,如果我們新添加一個UI界面;更換一種數據源的存儲和獲取方式,只需要修改對應層的代碼即可,領域模型層保持了穩定。
減少new引入的依賴及緊耦合最好的方式是使用構造函數註入依賴這種設計模式:即如果我們需要一個依賴的實例,通過構造函數註入。
解耦和最重要的原則就是依賴倒置原則:
高層模塊不應該依賴底層模塊,他們都應該依賴抽象。抽象不應該依賴於細節,細節應該依賴於抽象。
簡單理解就是組件應該依賴於介面而不是實現。
三、ABP依賴註入底層實現
ABP依賴註入是通過Castle Windsor依賴註入的框架實現。
1、通過實現IConventionalDependencyRegistrar的實例定義註入的約定,然後通過IocManager來讀取這個規則完成依賴註入
代碼在Abp項目文件的Dependency文件夾下
1)在PreInitialize方法中給IocManager的IConventionalDependencyRegistrar的list中加入BasicConventionalRegistrar
IocManager.AddConventionalRegistrar(new BasicConventionalRegistrar());
2)IocManager維護了一個叫_conventionalRegistrars的list,其中的元素類型就是IConventionalDependencyRegistrar。接著IocManager的RegisterAssemblyByConvention是在模塊的Initialize方法中被調用
public override void Initialize() { IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); }
3)IocManager在RegisterAssemblyByConvention方法中遍歷這個list,並根據IConventionalDependencyRegistrar的實例中定義的規則來完成register。
/// <summary> /// Registers types of given assembly by all conventional registrars. See <see cref="AddConventionalRegistrar"/> method. /// </summary> /// <param name="assembly">Assembly to register</param> public void RegisterAssemblyByConvention(Assembly assembly) { RegisterAssemblyByConvention(assembly, new ConventionalRegistrationConfig()); } /// <summary> /// Registers types of given assembly by all conventional registrars. See <see cref="AddConventionalRegistrar"/> method. /// </summary> /// <param name="assembly">Assembly to register</param> /// <param name="config">Additional configuration</param> public void RegisterAssemblyByConvention(Assembly assembly, ConventionalRegistrationConfig config) { var context = new ConventionalRegistrationContext(assembly, this, config); foreach (var registerer in _conventionalRegistrars) { registerer.RegisterAssembly(context); } if (config.InstallInstallers) { IocContainer.Install(FromAssembly.Instance(assembly)); } }
2、直接使用IocManager的Register方法直接完成註入
AbpModule有個受保護的IocManager的成員,所以AbpModule的派生類都可以使用這個IocManager完成註冊。
public class AbpWebModule : AbpModule { /// <inheritdoc/> public override void PreInitialize() { if (HttpContext.Current != null) { XmlLocalizationSource.RootDirectoryOfApplication = HttpContext.Current.Server.MapPath("~"); } //IocManager直接註入 IocManager.Register<IAbpWebModuleConfiguration, AbpWebModuleConfiguration>(); Configuration.Localization.Sources.Add( new DictionaryBasedLocalizationSource( AbpWebLocalizedMessages.SourceName, new XmlEmbeddedFileLocalizationDictionaryProvider( Assembly.GetExecutingAssembly(), "Abp.Web.Localization.AbpWebXmlSource" ))); } /// <inheritdoc/> public override void Initialize() { IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); } }