abp vnext2.0核心组件之模块加载组件源码解析

摘要:
Abpvnext是一个基于abp构建的微服务框架。老实说,当我读完核心组件的源代码时,我很兴奋。整个框架很好地利用了基于组件的思想,这真的是超级解耦的。整个框架的旧版本依赖于Castle,vnext将其解耦。它支持AutoFace或使用的默认容器。NetCore Vnext仍然遵循EFcore作为主要工具和其他ORM作为辅助工具的思想。当然,EFcore在实施DDD方面有优势。EventBus提供分布式版本

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是个值得使用的框架.下面开始回到正题.

1、模块加载系统

模块加载系统算是vnext的整个框架的入口,离了他,这个框架就废了.具体它有什么作用,看下面的代码分析,模块加载系统的入口如下:

abp vnext2.0核心组件之模块加载组件源码解析第1张

 每个应用框架必须要有一个启动模块类型,可以通过泛型或者Type实例传入,并且给定启动参数.

启动模块类型:虽然上面给定的约束是必须实现IAbpModule,但是大多数的实现情况是

abp vnext2.0核心组件之模块加载组件源码解析第2张

暂时不讲 AbpModule的源码,后面分析到具体的流程再做介绍.

ok,看看AbpApplicationFactory工厂做了什么了,通过名字分析很明显.AbpApplication的工厂.

abp vnext2.0核心组件之模块加载组件源码解析第3张

分析这个方法就能得出,只要传入启动模块的类型和DI的ServiceCollection和启动应用的参数,就能构建一个IAbpApplicationWithExternalServiceProvider,那么看看IAbpApplicationWithExternalServiceProvider都有什么

abp vnext2.0核心组件之模块加载组件源码解析第4张

 构建完成基本的实体后,调用Initialize方法初始化框架.再看看IAbpApplication接口

abp vnext2.0核心组件之模块加载组件源码解析第5张

 包含启动模块类型,DI注入集合、DI服务提供类,以及一个关闭应用程序必须执行的ShutDown方法.在看看IModuleContainer

abp vnext2.0核心组件之模块加载组件源码解析第6张

 包含模块集合,在Abp中,模块代表一个程序集.这里就是启动abp vnext框架的启动模块类型所依赖的所有模块类型,即所有的程序集集合你可以这样理解.因为一个Module类型(继承AbpModule类型或者实现IAbpModule接口的类型)代表一个程序集.且一个程序集只有一个Module类型(继承AbpModule类型或者实现IAbpModule接口的类型).

ok,接着回到上面的代码

abp vnext2.0核心组件之模块加载组件源码解析第7张

 此处省略一些无关核心流程的代码,代码如下:

abp vnext2.0核心组件之模块加载组件源码解析第8张

 简单的一些非空校验,这里有一个非常有趣的设计,如下:

abp vnext2.0核心组件之模块加载组件源码解析第9张

继续查看,如下

 abp vnext2.0核心组件之模块加载组件源码解析第10张

 ObjectAccessor源码如下:

abp vnext2.0核心组件之模块加载组件源码解析第11张

 类似装饰者模式,内部容纳一个类型.最后

abp vnext2.0核心组件之模块加载组件源码解析第12张

 ok,到这里整个流程大致就是,给IServiceProvider创建一个ObjectAccessor,且ObjectAccessor没有Value值,同时将ObjectAccessor写入DI,并做了简单的搜索优化.关于IServiceProvider的ObjectAccessor的作用,暂时不介绍,后续会说.

接着看如下代码:

abp vnext2.0核心组件之模块加载组件源码解析第13张

 初始化外部设置参数,接招向DI中注入IAbpApplication和IModuleContainer的单例对象.

接着看下面的代码:

abp vnext2.0核心组件之模块加载组件源码解析第14张

 abp vnext2.0核心组件之模块加载组件源码解析第15张

注入配置文件、日志、国际化等服务.接着看AddCoeAbpServices方法

abp vnext2.0核心组件之模块加载组件源码解析第16张

 abp vnext2.0核心组件之模块加载组件源码解析第17张

注入ModuleLoader(处理程序集间依赖关系,处理模块加载生命周期、的核心类型)、程序集发现类(所有程序集都能通过该类型拿到,只要程序集加入到了框架)、类型发现类(程序集集合所包含的所有类型)

abp vnext2.0核心组件之模块加载组件源码解析第18张

 初始化配置文件系统、等等操作,接着看如下代码,将上述类型写入DI

abp vnext2.0核心组件之模块加载组件源码解析第19张

 接下去这行代码就有趣了,如下:

abp vnext2.0核心组件之模块加载组件源码解析第20张

看看它干了什么,如下:

abp vnext2.0核心组件之模块加载组件源码解析第21张

 看看 services.GetConventionalRegistrars干了什么,如下:

abp vnext2.0核心组件之模块加载组件源码解析第22张

 很明显,从DI中读取程序集注册规则类列表,如果没有,则写入默认的程序集注册规则类.所以,这里如果你想自定义程序集注册规则,那么只需在有效的应用程序加载生命周期阶段注入自定义的程序集注册类即可,该类型必须实现下图所示接口

abp vnext2.0核心组件之模块加载组件源码解析第23张

 ok,这个扩展点讲完之后,看看默认的程序集注册规则类DefaultConventionalRegistrar干了什么,如下:

 abp vnext2.0核心组件之模块加载组件源码解析第24张

 很简单,自行阅读,再看看AddType的实现,如下:

abp vnext2.0核心组件之模块加载组件源码解析第25张

 abp vnext2.0核心组件之模块加载组件源码解析第26张

 支持类型跳过,如果类型打了DisableConventionalRegistrationAttribute特性,那么该类型将不会被写入DI.

abp vnext2.0核心组件之模块加载组件源码解析第27张

 如果当前类型没有打DependencyAttribute,或者打了DependencyAttribute特性,没有设置Lifetime,则当前类型也不会写入DI.

这里注意,根据代码可以发现,abp给类型生命周期的方式有两种,老版只有一种,如下:

第一种:

abp vnext2.0核心组件之模块加载组件源码解析第28张

 通过实现ISingletonDependency(单例注入),ITransientDependency(普通引用类型),IScopedDependency(范围内唯一)三大接口来表示当前类型的生命周期,老版abp也是使用这种方式,但是没有IScopedDependency

第二种:

abp vnext2.0核心组件之模块加载组件源码解析第29张

 通过DependencyAttribute特性,结构如下

abp vnext2.0核心组件之模块加载组件源码解析第30张

接着,如下代码

abp vnext2.0核心组件之模块加载组件源码解析第31张

abp vnext2.0核心组件之模块加载组件源码解析第32张

 如果当前类型打了ExposeServicesAttribute特性,那么则会调用该特性的如下方法

abp vnext2.0核心组件之模块加载组件源码解析第33张

 这个方法的用途是找出如果我们需要从DI中释出个类型,可以使用哪几种方式(常用的是接口,自身等),示例代码如下:

abp vnext2.0核心组件之模块加载组件源码解析第34张

 那么如果需要在框架中使用TestClass的实现,可以用ITestClass接口进行依赖注入,因为

abp vnext2.0核心组件之模块加载组件源码解析第35张

 当然这里可以写多个,因为

abp vnext2.0核心组件之模块加载组件源码解析第36张

 ExposeServicesAttribute特性中的IncludeDefaults和IncludeSelf属性是默认的策略,

IncludeDefaults设置为true是根据类型找出其实现的接口,且接口必须以I字母开头,且接口后面的名字必须和当前类型相等.如果匹配那么该接口有效,也可以进行依赖注入.

IncludeSelf设置为true,则可以通过当前类型进行依赖注入.

接着看如下代码

abp vnext2.0核心组件之模块加载组件源码解析第37张

 abp vnext2.0核心组件之模块加载组件源码解析第38张

 很简单,只需在有效的应用程序加载生命周期阶段注入指定的Action,注入方式如下:

abp vnext2.0核心组件之模块加载组件源码解析第39张

使用例子,类型映射,如下:

abp vnext2.0核心组件之模块加载组件源码解析第40张

最后看如下代码

abp vnext2.0核心组件之模块加载组件源码解析第41张

这段代码很简单,就不解释了.DependencyAttribute特性给上对应的值就能执行指定的操作,ok,到这里总结一下这种设计的用处,非常nice,原先老版abp注册系统核心单例类型是依赖castle的,如果换成这种设计方式,更加的灵活,如果我们需要给底层添加一个核心类,只需要创建一个类,然后配合Dependency特性和ExposeServices特性即可和DI完美集合,同时还提供了Action扩展,让你可以干很多的事情,就这一点,比老版abp好太多.到这里DefaultConventionalRegistrar介绍完毕

ok,在回到AddCoreAbpServices方法,如下:

abp vnext2.0核心组件之模块加载组件源码解析第42张

 这里也很简单,向DI中预先写入AbpModuleLifecycleOptions,该参数用于控制模块加载的生命周期,这四个Contributor分别对应模块加载生命周期的接口,

abp vnext2.0核心组件之模块加载组件源码解析第43张

 abp vnext2.0核心组件之模块加载组件源码解析第44张

 abp vnext2.0核心组件之模块加载组件源码解析第45张

 abp vnext2.0核心组件之模块加载组件源码解析第46张

 再看看核心Module的抽象

abp vnext2.0核心组件之模块加载组件源码解析第47张

 到这里肯定很多人很困惑,所以这里跳过一些流程,看下ModuleManager如何处理,如下

abp vnext2.0核心组件之模块加载组件源码解析第48张

 释出Contributor集合

abp vnext2.0核心组件之模块加载组件源码解析第49张

 abp vnext2.0核心组件之模块加载组件源码解析第50张

Contributor的作用很明显,模块加载生命周期中你可以执行的一些方法.这些方法会拿到一个ServiceProvider,即你可以操作DI,完成一些关键服务的操作.

关于模块加载的生命周期方法有哪些,如下

abp vnext2.0核心组件之模块加载组件源码解析第51张

 每个接口对应一个生命周期,这和老版Abp的设计也完全不同.优缺点暂时没发现.

接着,如下:

abp vnext2.0核心组件之模块加载组件源码解析第52张

abp vnext2.0核心组件之模块加载组件源码解析第53张

 调用ModuleLoader单例实例,执行加载模块的方法.核心算法和老版Abp一样,这里稍微解释下,

 核心点如下:

(1)、加载启动模块所有依赖的模块,并设置依赖项,最后生成IAbpModuleDescriptor集合

abp vnext2.0核心组件之模块加载组件源码解析第54张

 (2)、模块进行拓扑排序,进行循环依赖检测

abp vnext2.0核心组件之模块加载组件源码解析第55张

 ok,下面开始解析核心点源码

通过DependsOnAttribute特性来处理模块间的依赖关系.核心代码如下:

abp vnext2.0核心组件之模块加载组件源码解析第56张

 拿到当前类型的DependsOnAttribute特性,解析其内部的类型,加入到dependencies依赖类型集合.所以表示模块间的依赖关系根据如上代码可以得出两种模式,如下:

abp vnext2.0核心组件之模块加载组件源码解析第57张

 常用的是第二种.

通过上面的方法拿到所有的依赖类型集合之后,执行下面的递归方法

abp vnext2.0核心组件之模块加载组件源码解析第58张

 这样就可以遍历出所有的启动模块以来的所有模块.同时去除了重复的模块.最后遍历所有的模块生成如下类型的实例

abp vnext2.0核心组件之模块加载组件源码解析第59张

 模块实例的生命周期为单例,如下图:

abp vnext2.0核心组件之模块加载组件源码解析第60张

接着开始处理启动参数中配置的插件模块

 abp vnext2.0核心组件之模块加载组件源码解析第61张

 插件模块的三种添加方式如下

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添加插件类型,其核心参数如下:

abp vnext2.0核心组件之模块加载组件源码解析第62张

直接给文件夹路径+名称,扫描下面的插件程序集,并进行程序集过滤,核心的过滤方法如下:

abp vnext2.0核心组件之模块加载组件源码解析第63张

 最后,返回实现了AbpModule的核心模块类型

abp vnext2.0核心组件之模块加载组件源码解析第64张

 ok,接着回到模块加载系统的加载插件方法,如下:

abp vnext2.0核心组件之模块加载组件源码解析第65张

 abp vnext2.0核心组件之模块加载组件源码解析第66张

 ok,这里可以发现亮点

1、你可以同时添加多种形式的插件宿主,可以是文件夹下所有的插件程序集、可以是程序集解决方案、也可以是一个指定的程序集文件.Abp暂时提供了这三种,当然如果你有实力,也可以编写远程调用程序集插件.

2、和模块加载系统完成了集成,和上面的流程一样,加载出所有启动模块依赖的类型,并写入DI

abp vnext2.0核心组件之模块加载组件源码解析第67张

 ok,到这里插件模块介绍完毕.最后和普通模块一样生成IAbpModuleDescriptor集合

接着,拿到所有的模块集合之后(包括插件),开始设置所有模块间的依赖关系,如下,细心的会发现上面的

abp vnext2.0核心组件之模块加载组件源码解析第68张

 中有依赖集合.下面的代码就是整理这个关系的.

abp vnext2.0核心组件之模块加载组件源码解析第69张

 这里,逻辑很简单,就不介绍了,直接跳过,主要是通过DependsOnAttribute特性来实现.

接下去介绍核心点二模块进行拓扑排序,进行循环依赖检测

此时,我们拿到了一个完整的模块集合,内部的依赖关系也已经初步执行好.

abp vnext2.0核心组件之模块加载组件源码解析第70张

 核心代码如下,关于拓扑排序(算法的核心逻辑自行查阅代码,主要内容是按照依赖关系依次加入到集合,后期可一次执行,这样就可以集成生命周期),防止循环依赖就不说了,接着,将启动模块放到最后为了配合模块生命周期方法的执行.

ok,到这里两个核心点介绍完毕.

接下去.如下代码

abp vnext2.0核心组件之模块加载组件源码解析第71张

 abp vnext2.0核心组件之模块加载组件源码解析第72张

 生成如下上下文,并单例写入DI

abp vnext2.0核心组件之模块加载组件源码解析第73张

这个Item属性醉了,个人感觉没什么用,因为下面这个for循环

abp vnext2.0核心组件之模块加载组件源码解析第74张

 接着执行如下代码

abp vnext2.0核心组件之模块加载组件源码解析第75张

abp vnext2.0核心组件之模块加载组件源码解析第76张

 所以这两个生命周期接口执行的时间节点一定要记住.同时上下文会给你DI容器,方便你进行任何必须的类型操作.

接着

abp vnext2.0核心组件之模块加载组件源码解析第77张

 将当前模块类型对应的程序集中所有的类型写入DI,默认的注入规则上面已经介绍,默认的注册器类型为DefaultConventionalRegistrar.同时执行生命周期接口IZcfModule.

到这里已经执行的三个模块生命周期接口如下:

abp vnext2.0核心组件之模块加载组件源码解析第78张

 切记其执行的节点.

 接着开始初始化模块系统,注意,这边我跳过了DI容器切换的的内容(关于DI容器切换的源码分析后续的博文会介绍),代码如下:

abp vnext2.0核心组件之模块加载组件源码解析第79张

 从DI中释出单例ModuleManager类,执行如下初始化方法

abp vnext2.0核心组件之模块加载组件源码解析第80张

 这段代码进行简单的模块加载日志记录,后面的核心代码上面说过,执行预定义的模块生命周期方法,对应如下接口:

abp vnext2.0核心组件之模块加载组件源码解析第81张

执行这四个接口必须实现的方法,当然在AbpModule中都以virtual标记,所以你可以按照顺序一次进行一些类型操作.但是这几个生命周期函数,上下文只提供ServiceProvider,

功能做了限制.其余三个生命周期接口提供的是IServiceCollection实例,所以他们之间还是有差别的,除了执行顺序之外.

ok,到这里abp vnext2.0的核心模块记载系统核心流程源码分析结束了,纯属个人理解,能力有限,有问题请指正!

下一篇会介绍vnext如何完成整个DI切换,换成autofac或者其他容器.以及如何和模块加载系统结合.

免责声明:文章转载自《abp vnext2.0核心组件之模块加载组件源码解析》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇解决Ubuntu的root账号无法登录SSH问题-Permission denied, please try again.Float32与Float16转换下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

源码编译安装nginx及设置开机启动项

1、上传nginx文档;解压到/data目录下,并安装依赖包tar xf nginx-1.20.1.tar.gz -C /data/cd /data/nginx-1.20.1/ && ll依赖关系安装,执行如下:yum -y install zlib pcre pcre-devel openssl openssl-devel 2、自定义安装...

jdk/java版本与Android源码编译中的错误

错误一:javap未指向有效的java版本 Traceback (most recent call last): File "../../base/android/jni_generator/jni_generator.py", line 1065, in <module> sys.exit(main(sys.argv)) Fi...

Flink 源码(二): Flink Client 实现原理与源码解析(一)

来源:https://mp.weixin.qq.com/s/WiRyQEoDfuowT3LNfZ-NSw 0 本文大纲: 一、我们本次的目的是什么? 这次我们的目的是,在本地的 IDEA 中去 debug flink-clients 代码,然后远程提交给 flink standalone 集群上去执行,看一看 flink 客户端在提交代码之前都干了什么。...

实验 1 : Mininet 源码安装 和可视化拓扑工具

实验 1 : Mininet 源码安装 和可视化拓扑工具 一、 实验 目的 掌握 Mininet 的源码安装方法和 miniedit 可视化拓扑生成工具。 二 、实验 任务 使用源码安装 Mininet 的 2.3.0d6 版本,并使用可视化拓扑工具生成一个最简拓扑(1 台交换机连接 2 台主机)。 三 、 实验步骤 1. 实验环境 安装了...

Alibaba Sentinel 限流与熔断初探(技巧篇)

温馨提示:源码分析 Alibaba Sentinel 专栏开始连载,本文展示如何学习一个全新的技术的方法。该专栏基于 1.7.0 版本。 在学习一个新技术或新框架时,建议先查看其官方文档, Sentinel 官方文档链接如下:官方文档,以获得对其形成一个整体的认识。 @ 目录 1、Sentinel 是什么 ?主要能解决什么问题? 2、限流与熔断的使用场...

k8s apiserver 源码阅读笔记

更好的阅读体验建议点击下方原文链接。 原文: http://maoqide.live/post/cloud/apiserver-%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/ k8s apiserver 源码阅读笔记 代码结构 本部分用于记录 apiserver 代码整体结构及关键方法,便于到源码中查找,个人阅读记录,读者可跳过...