未能加载文件或程序集rsy3_abp vnext2.0之核心组件模块加载系统源码解析
abp vnext是abp官方在abp的基礎(chǔ)之上構(gòu)建的微服務(wù)架構(gòu),說(shuō)實(shí)話,看完核心組件源碼的時(shí)候,很興奮,整個(gè)框架將組件化的細(xì)想運(yùn)用的很好,真的超級(jí)解耦.老版整個(gè)框架依賴Castle的問(wèn)題,vnext對(duì)其進(jìn)行了解耦,支持AutoFac或者使用.Net Core的默認(rèn)容器.vnext依然沿用EF core為主,其余ORM為輔助的思想,當(dāng)然EF core來(lái)實(shí)現(xiàn)DDD確實(shí)有優(yōu)勢(shì),EventBus提供了分布式版本,并提供了RabbitMQ的實(shí)現(xiàn)版本,Aop攔截器依然采用Castle.Core.AsyncInterceptor.這一點(diǎn)Dora.Interception貌似可以解決,估計(jì)如果高度組件化,那么這也是一個(gè)擴(kuò)展點(diǎn).整個(gè)模塊加載系統(tǒng)更加的完善,提供了跟多可選擇的特性,工作單元也進(jìn)行了小幅度的重構(gòu),代碼更加的通俗易懂(在實(shí)現(xiàn)異步工作單元嵌套的設(shè)計(jì)就有體現(xiàn))等等還有很多,當(dāng)然不是本文的重點(diǎn),vnext2.0是個(gè)值得使用的框架.下面開(kāi)始回到正題.
1、模塊加載系統(tǒng)
模塊加載系統(tǒng)算是vnext的整個(gè)框架的入口,離了他,這個(gè)框架就廢了.具體它有什么作用,看下面的代碼分析,模塊加載系統(tǒng)的入口如下:
?每個(gè)應(yīng)用框架必須要有一個(gè)啟動(dòng)模塊類型,可以通過(guò)泛型或者Type實(shí)例傳入,并且給定啟動(dòng)參數(shù).
啟動(dòng)模塊類型:雖然上面給定的約束是必須實(shí)現(xiàn)IAbpModule,但是大多數(shù)的實(shí)現(xiàn)情況是
暫時(shí)不講?AbpModule的源碼,后面分析到具體的流程再做介紹.
ok,看看AbpApplicationFactory工廠做了什么了,通過(guò)名字分析很明顯.AbpApplication的工廠.
分析這個(gè)方法就能得出,只要傳入啟動(dòng)模塊的類型和DI的ServiceCollection和啟動(dòng)應(yīng)用的參數(shù),就能構(gòu)建一個(gè)IAbpApplicationWithExternalServiceProvider,那么看看IAbpApplicationWithExternalServiceProvider都有什么
?構(gòu)建完成基本的實(shí)體后,調(diào)用Initialize方法初始化框架.再看看IAbpApplication接口
?包含啟動(dòng)模塊類型,DI注入集合、DI服務(wù)提供類,以及一個(gè)關(guān)閉應(yīng)用程序必須執(zhí)行的ShutDown方法.在看看IModuleContainer
?包含模塊集合,在Abp中,模塊代表一個(gè)程序集.這里就是啟動(dòng)abp vnext框架的啟動(dòng)模塊類型所依賴的所有模塊類型,即所有的程序集集合你可以這樣理解.因?yàn)橐粋€(gè)Module類型(繼承AbpModule類型或者實(shí)現(xiàn)IAbpModule接口的類型)代表一個(gè)程序集.且一個(gè)程序集只有一個(gè)Module類型(繼承AbpModule類型或者實(shí)現(xiàn)IAbpModule接口的類型).
ok,接著回到上面的代碼
?此處省略一些無(wú)關(guān)核心流程的代碼,代碼如下:
?簡(jiǎn)單的一些非空校驗(yàn),這里有一個(gè)非常有趣的設(shè)計(jì),如下:
繼續(xù)查看,如下
?
?ObjectAccessor源碼如下:
?類似裝飾者模式,內(nèi)部容納一個(gè)類型.最后
?ok,到這里整個(gè)流程大致就是,給IServiceProvider創(chuàng)建一個(gè)ObjectAccessor,且ObjectAccessor沒(méi)有Value值,同時(shí)將ObjectAccessor寫入DI,并做了簡(jiǎn)單的搜索優(yōu)化.關(guān)于IServiceProvider的ObjectAccessor的作用,暫時(shí)不介紹,后續(xù)會(huì)說(shuō).
接著看如下代碼:
?初始化外部設(shè)置參數(shù),接招向DI中注入IAbpApplication和IModuleContainer的單例對(duì)象.
接著看下面的代碼:
?
注入配置文件、日志、國(guó)際化等服務(wù).接著看AddCoeAbpServices方法
?
注入ModuleLoader(處理程序集間依賴關(guān)系,處理模塊加載生命周期、的核心類型)、程序集發(fā)現(xiàn)類(所有程序集都能通過(guò)該類型拿到,只要程序集加入到了框架)、類型發(fā)現(xiàn)類(程序集集合所包含的所有類型)
?初始化配置文件系統(tǒng)、等等操作,接著看如下代碼,將上述類型寫入DI
?接下去這行代碼就有趣了,如下:
看看它干了什么,如下:
?看看?services.GetConventionalRegistrars干了什么,如下:
?很明顯,從DI中讀取程序集注冊(cè)規(guī)則類列表,如果沒(méi)有,則寫入默認(rèn)的程序集注冊(cè)規(guī)則類.所以,這里如果你想自定義程序集注冊(cè)規(guī)則,那么只需在有效的應(yīng)用程序加載生命周期階段注入自定義的程序集注冊(cè)類即可,該類型必須實(shí)現(xiàn)下圖所示接口
?ok,這個(gè)擴(kuò)展點(diǎn)講完之后,看看默認(rèn)的程序集注冊(cè)規(guī)則類DefaultConventionalRegistrar干了什么,如下:
?
?很簡(jiǎn)單,自行閱讀,再看看AddType的實(shí)現(xiàn),如下:
?
?支持類型跳過(guò),如果類型打了DisableConventionalRegistrationAttribute特性,那么該類型將不會(huì)被寫入DI.
?如果當(dāng)前類型沒(méi)有打DependencyAttribute,或者打了DependencyAttribute特性,沒(méi)有設(shè)置Lifetime,則當(dāng)前類型也不會(huì)寫入DI.
這里注意,根據(jù)代碼可以發(fā)現(xiàn),abp給類型生命周期的方式有兩種,老版只有一種,如下:
第一種:
?通過(guò)實(shí)現(xiàn)ISingletonDependency(單例注入),ITransientDependency(普通引用類型),IScopedDependency(范圍內(nèi)唯一)三大接口來(lái)表示當(dāng)前類型的生命周期,老版abp也是使用這種方式,但是沒(méi)有IScopedDependency
第二種:
?
?通過(guò)DependencyAttribute特性,結(jié)構(gòu)如下
接著,如下代碼
?如果當(dāng)前類型打了ExposeServicesAttribute特性,那么則會(huì)調(diào)用該特性的如下方法
?這個(gè)方法的用途是找出如果我們需要從DI中釋出個(gè)類型,可以使用哪幾種方式(常用的是接口,自身等),示例代碼如下:
?那么如果需要在框架中使用TestClass的實(shí)現(xiàn),可以用ITestClass接口進(jìn)行依賴注入,因?yàn)?/p>
?當(dāng)然這里可以寫多個(gè),因?yàn)?/p>
?ExposeServicesAttribute特性中的IncludeDefaults和IncludeSelf屬性是默認(rèn)的策略,
IncludeDefaults設(shè)置為true是根據(jù)類型找出其實(shí)現(xiàn)的接口,且接口必須以I字母開(kāi)頭,且接口后面的名字必須和當(dāng)前類型相等.如果匹配那么該接口有效,也可以進(jìn)行依賴注入.
IncludeSelf設(shè)置為true,則可以通過(guò)當(dāng)前類型進(jìn)行依賴注入.
接著看如下代碼
?
?很簡(jiǎn)單,只需在有效的應(yīng)用程序加載生命周期階段注入指定的Action,注入方式如下:
使用例子,類型映射,如下:
最后看如下代碼
這段代碼很簡(jiǎn)單,就不解釋了.DependencyAttribute特性給上對(duì)應(yīng)的值就能執(zhí)行指定的操作,ok,到這里總結(jié)一下這種設(shè)計(jì)的用處,非常nice,原先老版abp注冊(cè)系統(tǒng)核心單例類型是依賴castle的,如果換成這種設(shè)計(jì)方式,更加的靈活,如果我們需要給底層添加一個(gè)核心類,只需要?jiǎng)?chuàng)建一個(gè)類,然后配合Dependency特性和ExposeServices特性即可和DI完美集合,同時(shí)還提供了Action擴(kuò)展,讓你可以干很多的事情,就這一點(diǎn),比老版abp好太多.到這里DefaultConventionalRegistrar介紹完畢
ok,在回到AddCoreAbpServices方法,如下:
?這里也很簡(jiǎn)單,向DI中預(yù)先寫入AbpModuleLifecycleOptions,該參數(shù)用于控制模塊加載的生命周期,這四個(gè)Contributor分別對(duì)應(yīng)模塊加載生命周期的接口,
?
?
?
?再看看核心Module的抽象
?到這里肯定很多人很困惑,所以這里跳過(guò)一些流程,看下ModuleManager如何處理,如下
?釋出Contributor集合
?
Contributor的作用很明顯,模塊加載生命周期中你可以執(zhí)行的一些方法.這些方法會(huì)拿到一個(gè)ServiceProvider,即你可以操作DI,完成一些關(guān)鍵服務(wù)的操作.
關(guān)于模塊加載的生命周期方法有哪些,如下
?每個(gè)接口對(duì)應(yīng)一個(gè)生命周期,這和老版Abp的設(shè)計(jì)也完全不同.優(yōu)缺點(diǎn)暫時(shí)沒(méi)發(fā)現(xiàn).
接著,如下:
?調(diào)用ModuleLoader單例實(shí)例,執(zhí)行加載模塊的方法.核心算法和老版Abp一樣,這里稍微解釋下,
?核心點(diǎn)如下:
(1)、加載啟動(dòng)模塊所有依賴的模塊,并設(shè)置依賴項(xiàng),最后生成IAbpModuleDescriptor集合
?(2)、模塊進(jìn)行拓?fù)渑判?進(jìn)行循環(huán)依賴檢測(cè)
?ok,下面開(kāi)始解析核心點(diǎn)源碼
通過(guò)DependsOnAttribute特性來(lái)處理模塊間的依賴關(guān)系.核心代碼如下:
?拿到當(dāng)前類型的DependsOnAttribute特性,解析其內(nèi)部的類型,加入到dependencies依賴類型集合.所以表示模塊間的依賴關(guān)系根據(jù)如上代碼可以得出兩種模式,如下:
?常用的是第二種.
通過(guò)上面的方法拿到所有的依賴類型集合之后,執(zhí)行下面的遞歸方法
?這樣就可以遍歷出所有的啟動(dòng)模塊以來(lái)的所有模塊.同時(shí)去除了重復(fù)的模塊.最后遍歷所有的模塊生成如下類型的實(shí)例
?模塊實(shí)例的生命周期為單例,如下圖:
接著開(kāi)始處理啟動(dòng)參數(shù)中配置的插件模塊
?
?插件模塊的三種添加方式如下
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添加插件類型,其核心參數(shù)如下:
直接給文件夾路徑+名稱,掃描下面的插件程序集,并進(jìn)行程序集過(guò)濾,核心的過(guò)濾方法如下:
?最后,返回實(shí)現(xiàn)了AbpModule的核心模塊類型
?ok,接著回到模塊加載系統(tǒng)的加載插件方法,如下:
?
?ok,這里可以發(fā)現(xiàn)亮點(diǎn)
1、你可以同時(shí)添加多種形式的插件宿主,可以是文件夾下所有的插件程序集、可以是程序集解決方案、也可以是一個(gè)指定的程序集文件.Abp暫時(shí)提供了這三種,當(dāng)然如果你有實(shí)力,也可以編寫遠(yuǎn)程調(diào)用程序集插件.
2、和模塊加載系統(tǒng)完成了集成,和上面的流程一樣,加載出所有啟動(dòng)模塊依賴的類型,并寫入DI
?ok,到這里插件模塊介紹完畢.最后和普通模塊一樣生成IAbpModuleDescriptor集合
接著,拿到所有的模塊集合之后(包括插件),開(kāi)始設(shè)置所有模塊間的依賴關(guān)系,如下,細(xì)心的會(huì)發(fā)現(xiàn)上面的
?中有依賴集合.下面的代碼就是整理這個(gè)關(guān)系的.
?這里,邏輯很簡(jiǎn)單,就不介紹了,直接跳過(guò),主要是通過(guò)DependsOnAttribute特性來(lái)實(shí)現(xiàn).
接下去介紹核心點(diǎn)二模塊進(jìn)行拓?fù)渑判?進(jìn)行循環(huán)依賴檢測(cè)
此時(shí),我們拿到了一個(gè)完整的模塊集合,內(nèi)部的依賴關(guān)系也已經(jīng)初步執(zhí)行好.
?核心代碼如下,關(guān)于拓?fù)渑判?算法的核心邏輯自行查閱代碼,主要內(nèi)容是按照依賴關(guān)系依次加入到集合,后期可一次執(zhí)行,這樣就可以集成生命周期),防止循環(huán)依賴就不說(shuō)了,接著,將啟動(dòng)模塊放到最后為了配合模塊生命周期方法的執(zhí)行.
ok,到這里兩個(gè)核心點(diǎn)介紹完畢.
接下去.如下代碼
?
?生成如下上下文,并單例寫入DI
這個(gè)Item屬性醉了,個(gè)人感覺(jué)沒(méi)什么用,因?yàn)橄旅孢@個(gè)for循環(huán)
?接著執(zhí)行如下代碼
?所以這兩個(gè)生命周期接口執(zhí)行的時(shí)間節(jié)點(diǎn)一定要記住.同時(shí)上下文會(huì)給你DI容器,方便你進(jìn)行任何必須的類型操作.
接著
?將當(dāng)前模塊類型對(duì)應(yīng)的程序集中所有的類型寫入DI,默認(rèn)的注入規(guī)則上面已經(jīng)介紹,默認(rèn)的注冊(cè)器類型為DefaultConventionalRegistrar.同時(shí)執(zhí)行生命周期接口IZcfModule.
到這里已經(jīng)執(zhí)行的三個(gè)模塊生命周期接口如下:
?切記其執(zhí)行的節(jié)點(diǎn).
?接著開(kāi)始初始化模塊系統(tǒng),注意,這邊我跳過(guò)了DI容器切換的的內(nèi)容(關(guān)于DI容器切換的源碼分析后續(xù)的博文會(huì)介紹),代碼如下:
?從DI中釋出單例ModuleManager類,執(zhí)行如下初始化方法
?這段代碼進(jìn)行簡(jiǎn)單的模塊加載日志記錄,后面的核心代碼上面說(shuō)過(guò),執(zhí)行預(yù)定義的模塊生命周期方法,對(duì)應(yīng)如下接口:
執(zhí)行這四個(gè)接口必須實(shí)現(xiàn)的方法,當(dāng)然在AbpModule中都以virtual標(biāo)記,所以你可以按照順序一次進(jìn)行一些類型操作.但是這幾個(gè)生命周期函數(shù),上下文只提供ServiceProvider,
功能做了限制.其余三個(gè)生命周期接口提供的是IServiceCollection實(shí)例,所以他們之間還是有差別的,除了執(zhí)行順序之外.
ok,到這里abp vnext2.0的核心模塊記載系統(tǒng)核心流程源碼分析結(jié)束了,純屬個(gè)人理解,能力有限,有問(wèn)題請(qǐng)指正!
下一篇會(huì)介紹vnext如何完成整個(gè)DI切換,換成autofac或者其他容器.以及如何和模塊加載系統(tǒng)結(jié)合.
總結(jié)
以上是生活随笔為你收集整理的未能加载文件或程序集rsy3_abp vnext2.0之核心组件模块加载系统源码解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: android 图片浏览控件_Andro
- 下一篇: oracle帮助系统,开启Oracle的