abp vnext是abp官方在abp的基礎之上構建的微服務框架,說實話,看完核心組件源碼的時候,很興奮,整個框架將組件化的細想運用的很好,真的超級解耦.老版整個框架依賴Castle的問題,vnext對其進行了解耦,支持AutoFac或者使用.Net Core的默認容器.vnext依然沿用EF core爲主,其他ORM爲輔助的思想,固然EF core來實現DDD確實有優點,EventBus提供了分佈式版本,並提供了RabbitMQ的實現版本,Aop攔截器依然採用Castle.Core.AsyncInterceptor.這一點Dora.Interception貌似能夠解決,估計若是高度組件化,那麼這也是一個擴展點.整個模塊加載系統更加的完善,提供了跟多可選擇的特性,工做單元也進行了小幅度的重構,代碼更加的通俗易懂(在實現異步工做單元嵌套的設計就有體現)等等還有不少,固然不是本文的重點,vnext2.0是個值得使用的框架.下面開始回到正題.算法
一、模塊加載系統框架
模塊加載系統算是vnext的整個框架的入口,離了他,這個框架就廢了.具體它有什麼做用,看下面的代碼分析,模塊加載系統的入口以下:異步
每一個應用框架必需要有一個啓動模塊類型,能夠經過泛型或者Type實例傳入,而且給定啓動參數.分佈式
啓動模塊類型:雖然上面給定的約束是必須實現IAbpModule,可是大多數的實現狀況是ide
暫時不講 AbpModule的源碼,後面分析到具體的流程再作介紹.函數
ok,看看AbpApplicationFactory工廠作了什麼了,經過名字分析很明顯.AbpApplication的工廠.微服務
分析這個方法就能得出,只要傳入啓動模塊的類型和DI的ServiceCollection和啓動應用的參數,就能構建一個IAbpApplicationWithExternalServiceProvider,那麼看看IAbpApplicationWithExternalServiceProvider都有什麼源碼分析
構建完成基本的實體後,調用Initialize方法初始化框架.再看看IAbpApplication接口組件化
包含啓動模塊類型,DI注入集合、DI服務提供類,以及一個關閉應用程序必須執行的ShutDown方法.在看看IModuleContainer優化
包含模塊集合,在Abp中,模塊表明一個程序集.這裏就是啓動abp vnext框架的啓動模塊類型所依賴的全部模塊類型,即全部的程序集集合你能夠這樣理解.由於一個Module類型(繼承AbpModule類型或者實現IAbpModule接口的類型)表明一個程序集.且一個程序集只有一個Module類型(繼承AbpModule類型或者實現IAbpModule接口的類型).
ok,接着回到上面的代碼
此處省略一些無關核心流程的代碼,代碼以下:
簡單的一些非空校驗,這裏有一個很是有趣的設計,以下:
繼續查看,以下
ObjectAccessor源碼以下:
相似裝飾者模式,內部容納一個類型.最後
ok,到這裏整個流程大體就是,給IServiceProvider建立一個ObjectAccessor,且ObjectAccessor沒有Value值,同時將ObjectAccessor寫入DI,並作了簡單的搜索優化.關於IServiceProvider的ObjectAccessor的做用,暫時不介紹,後續會說.
接着看以下代碼:
初始化外部設置參數,接招向DI中注入IAbpApplication和IModuleContainer的單例對象.
接着看下面的代碼:
注入配置文件、日誌、國際化等服務.接着看AddCoeAbpServices方法
注入ModuleLoader(處理程序集間依賴關係,處理模塊加載生命週期、的核心類型)、程序集發現類(全部程序集都能經過該類型拿到,只要程序集加入到了框架)、類型發現類(程序集集合所包含的全部類型)
初始化配置文件系統、等等操做,接着看以下代碼,將上述類型寫入DI
接下去這行代碼就有趣了,以下:
看看它幹了什麼,以下:
看看 services.GetConventionalRegistrars幹了什麼,以下:
很明顯,從DI中讀取程序集註冊規則類列表,若是沒有,則寫入默認的程序集註冊規則類.因此,這裏若是你想自定義程序集註冊規則,那麼只需在有效的應用程序加載生命週期階段注入自定義的程序集註冊類便可,該類型必須實現下圖所示接口
ok,這個擴展點講完以後,看看默認的程序集註冊規則類DefaultConventionalRegistrar幹了什麼,以下:
很簡單,自行閱讀,再看看AddType的實現,以下:
支持類型跳過,若是類型打了DisableConventionalRegistrationAttribute特性,那麼該類型將不會被寫入DI.
若是當前類型沒有打DependencyAttribute,或者打了DependencyAttribute特性,沒有設置Lifetime,則當前類型也不會寫入DI.
這裏注意,根據代碼能夠發現,abp給類型生命週期的方式有兩種,老版只有一種,以下:
第一種:
經過實現ISingletonDependency(單例注入),ITransientDependency(普通引用類型),IScopedDependency(範圍內惟一)三大接口來表示當前類型的生命週期,老版abp也是使用這種方式,可是沒有IScopedDependency
第二種:
經過DependencyAttribute特性,結構以下
接着,以下代碼
若是當前類型打了ExposeServicesAttribute特性,那麼則會調用該特性的以下方法
這個方法的用途是找出若是咱們須要從DI中釋出個類型,可使用哪幾種方式(經常使用的是接口,自身等),示例代碼以下:
那麼若是須要在框架中使用TestClass的實現,能夠用ITestClass接口進行依賴注入,由於
固然這裏能夠寫多個,由於
ExposeServicesAttribute特性中的IncludeDefaults和IncludeSelf屬性是默認的策略,
IncludeDefaults設置爲true是根據類型找出其實現的接口,且接口必須以I字母開頭,且接口後面的名字必須和當前類型相等.若是匹配那麼該接口有效,也能夠進行依賴注入.
IncludeSelf設置爲true,則能夠經過當前類型進行依賴注入.
接着看以下代碼
很簡單,只需在有效的應用程序加載生命週期階段注入指定的Action,注入方式以下:
使用例子,類型映射,以下:
最後看以下代碼
這段代碼很簡單,就不解釋了.DependencyAttribute特性給上對應的值就能執行指定的操做,ok,到這裏總結一下這種設計的用處,很是nice,原先老版abp註冊系統核心單例類型是依賴castle的,若是換成這種設計方式,更加的靈活,若是咱們須要給底層添加一個核心類,只須要建立一個類,而後配合Dependency特性和ExposeServices特性便可和DI完美集合,同時還提供了Action擴展,讓你能夠幹不少的事情,就這一點,比老版abp好太多.到這裏DefaultConventionalRegistrar介紹完畢
ok,在回到AddCoreAbpServices方法,以下:
這裏也很簡單,向DI中預先寫入AbpModuleLifecycleOptions,該參數用於控制模塊加載的生命週期,這四個Contributor分別對應模塊加載生命週期的接口,
再看看核心Module的抽象
到這裏確定不少人很困惑,因此這裏跳過一些流程,看下ModuleManager如何處理,以下
釋出Contributor集合
Contributor的做用很明顯,模塊加載生命週期中你能夠執行的一些方法.這些方法會拿到一個ServiceProvider,即你能夠操做DI,完成一些關鍵服務的操做.
關於模塊加載的生命週期方法有哪些,以下
每一個接口對應一個生命週期,這和老版Abp的設計也徹底不一樣.優缺點暫時沒發現.
接着,以下:
調用ModuleLoader單例實例,執行加載模塊的方法.核心算法和老版Abp同樣,這裏稍微解釋下,
核心點以下:
(1)、加載啓動模塊全部依賴的模塊,並設置依賴項,最後生成IAbpModuleDescriptor集合
(2)、模塊進行拓撲排序,進行循環依賴檢測
ok,下面開始解析核心點源碼
經過DependsOnAttribute特性來處理模塊間的依賴關係.核心代碼以下:
拿到當前類型的DependsOnAttribute特性,解析其內部的類型,加入到dependencies依賴類型集合.因此表示模塊間的依賴關係根據如上代碼能夠得出兩種模式,以下:
經常使用的是第二種.
經過上面的方法拿到全部的依賴類型集合以後,執行下面的遞歸方法
這樣就能夠遍歷出全部的啓動模塊以來的全部模塊.同時去除了重複的模塊.最後遍歷全部的模塊生成以下類型的實例
模塊實例的生命週期爲單例,以下圖:
接着開始處理啓動參數中配置的插件模塊
插件模塊的三種添加方式以下
public static class PlugInSourceListExtensions
{
public static void AddFolder( [NotNull] this PlugInSourceList list, [NotNull] string folder, SearchOption searchOption = SearchOption.TopDirectoryOnly) { Check.NotNull(list, nameof(list)); list.Add(new FolderPlugInSource(folder, searchOption)); } public static void AddTypes( [NotNull] this PlugInSourceList list, params Type[] moduleTypes) { Check.NotNull(list, nameof(list)); list.Add(new TypePlugInSource(moduleTypes)); } public static void AddFiles( [NotNull] this PlugInSourceList list, params string[] filePaths) { Check.NotNull(list, nameof(list)); list.Add(new FilePlugInSource(filePaths)); } }
這邊只介紹一種,其他核心流程都同樣,以下:
FolderPlugInSource添加插件類型,其核心參數以下:
直接給文件夾路徑+名稱,掃描下面的插件程序集,並進行程序集過濾,核心的過濾方法以下:
最後,返回實現了AbpModule的核心模塊類型
ok,接着回到模塊加載系統的加載插件方法,以下:
ok,這裏能夠發現亮點
一、你能夠同時添加多種形式的插件宿主,能夠是文件夾下全部的插件程序集、能夠是程序集解決方案、也能夠是一個指定的程序集文件.Abp暫時提供了這三種,固然若是你有實力,也能夠編寫遠程調用程序集插件.
二、和模塊加載系統完成了集成,和上面的流程同樣,加載出全部啓動模塊依賴的類型,並寫入DI
ok,到這裏插件模塊介紹完畢.最後和普通模塊同樣生成IAbpModuleDescriptor集合
接着,拿到全部的模塊集合以後(包括插件),開始設置全部模塊間的依賴關係,以下,細心的會發現上面的
中有依賴集合.下面的代碼就是整理這個關係的.
這裏,邏輯很簡單,就不介紹了,直接跳過,主要是經過DependsOnAttribute特性來實現.
接下去介紹核心點二模塊進行拓撲排序,進行循環依賴檢測
此時,咱們拿到了一個完整的模塊集合,內部的依賴關係也已經初步執行好.
核心代碼以下,關於拓撲排序(算法的核心邏輯自行查閱代碼,主要內容是按照依賴關係依次加入到集合,後期可一次執行,這樣就能夠集成生命週期),防止循環依賴就不說了,接着,將啓動模塊放到最後爲了配合模塊生命週期方法的執行.
ok,到這裏兩個核心點介紹完畢.
接下去.以下代碼
生成以下上下文,並單例寫入DI
這個Item屬性醉了,我的感受沒什麼用,由於下面這個for循環
接着執行以下代碼
因此這兩個生命週期接口執行的時間節點必定要記住.同時上下文會給你DI容器,方便你進行任何須須的類型操做.
接着
將當前模塊類型對應的程序集中全部的類型寫入DI,默認的注入規則上面已經介紹,默認的註冊器類型爲DefaultConventionalRegistrar.同時執行生命週期接口IZcfModule.
到這裏已經執行的三個模塊生命週期接口以下:
切記其執行的節點.
接着開始初始化模塊系統,注意,這邊我跳過了DI容器切換的的內容(關於DI容器切換的源碼分析後續的博文會介紹),代碼以下:
從DI中釋出單例ModuleManager類,執行以下初始化方法
這段代碼進行簡單的模塊加載日誌記錄,後面的核心代碼上面說過,執行預約義的模塊生命週期方法,對應以下接口:
執行這四個接口必須實現的方法,固然在AbpModule中都以virtual標記,因此你能夠按照順序一次進行一些類型操做.可是這幾個生命週期函數,上下文只提供ServiceProvider,
功能作了限制.其他三個生命週期接口提供的是IServiceCollection實例,因此他們之間仍是有差異的,除了執行順序以外.
ok,到這裏abp vnext2.0的核心模塊記載系統核心流程源碼分析結束了,純屬我的理解,能力有限,有問題請指正!
下一篇會介紹vnext如何完成整個DI切換,換成autofac或者其餘容器.以及如何和模塊加載系統結合.