[Abp vNext 源码分析] - 1. 框架启动流程分析
一、简要说明
本篇文章主要剖析与讲解 Abp vNext 在 Web API 项目下的启动流程,让大家了解整个 Abp vNext 框架是如何运作的。总的来说 ,Abp vNext 比起 ABP 框架更加精简。因为在 vNext 版本当中,原来归属于 Abp 库的许多内置的基本组件 (组织单元、拦截器等) 被拆分成了单独的模块,这样我们来看它整个启动流程就更加地直观清晰。
二、源码分析
要分析其源码,我这里是从他官方的 Demo 模板入手的,你可以在 https://abp.io 上构建你自己的模板项目。工具上我使用的是 Jetbrains 家的 Rider,配置好符号服务器(External Symbols Server),我们就能够直接调试其底层源码。(因为 Abp vNext 项目使用了 Source Link)
2.1 Startup 文件的入口点
这里我选择的项目是 Web API,直接来到其 Startup.cs 文件,我们就可以看到在 Startup 类当中的 Configure() 与 ConfigureService() 方法内部我们注入并启用了 Abp vNext 框架。
public class Startup
{
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// 注入 Abp 相关的服务。
services.AddApplication<DemoAppModule>(options =>
{
options.UseAutofac();
});
// 接管自带的 IoC Container。
return services.BuildServiceProviderFromFactory();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// 配置 ASP.NET Core Mvc 相关参数。
app.InitializeApplication();
}
}
在上面我们可以看到,ABP vNext 在注入服务的时候支持传入一个 Action<AbpApplicationCreationOptions> 委托。上述代码中,这个委托内部使用了 UseAutoFac() 将 AutoFac 的容器注入到了 MS IoC 当中,关于这块代码下文会着重讲解。
2.2 Abp 服务注册
在上一节看到的服务注册代码,是通过扩展 IServiceCollection 接口编写的一个扩展方法实现的,在方法内部是通过 AbpApplicationFactory 静态工厂来创建一个 AbpApplicationBase 实例。
public static class ServiceCollectionApplicationExtensions
{
public static IAbpApplicationWithExternalServiceProvider AddApplication<TStartupModule>(
[NotNull] this IServiceCollection services,
[CanBeNull] Action<AbpApplicationCreationOptions> optionsAction = null)
where TStartupModule : IAbpModule
{
return AbpApplicationFactory.Create<TStartupModule>(services, optionsAction);
}
// ... 其他代码
}
在这个方法当中,通过名字 WithExternalServiceProvider 我们就知道,这个 Applictaion 是依赖于外部的 IServiceProvider 实例。
提示:
它继承的
AbpApplicationBase基类还拥有另外一个实现,即AbpApplicationWithInternalServiceProvider类型,该类型一般 用于控制台程序,它会在 Abp vNext 框架内自行构建一个IServiceProvider对象。
我们回到之前的代码,在这个 AbpApplicationWithExternalServiceProvider 类型内部的构造方法很简单,只是通过 IServiceCollection 对象把自己注入到了服务集合当中。
internal class AbpApplicationWithExternalServiceProvider : AbpApplicationBase, IAbpApplicationWithExternalServiceProvider
{
public AbpApplicationWithExternalServiceProvider(
[NotNull] Type startupModuleType,
[NotNull] IServiceCollection services,
[CanBeNull] Action<AbpApplicationCreationOptions> optionsAction
) : base(
startupModuleType,
services,
optionsAction)
{
// 注入自己到 IoC 当中。
services.AddSingleton<IAbpApplicationWithExternalServiceProvider>(this);
}
// 执行框架初始化操作,主要工作是加载模块并执行其初始化方法。
public void Initialize(IServiceProvider serviceProvider)
{
Check.NotNull(serviceProvider, nameof(serviceProvider));
SetServiceProvider(serviceProvider);
InitializeModules();
}
}
重点代码在于它的基类构造函数,在基类构造函数当中 Abp vNext 注入了诸多 ASP.NET Core 需要的日志服务、本地化服务等。并且它也抽象出了一个 IModuleLoader,用于辅助我们加载模块。
internal AbpApplicationBase(
[NotNull] Type startupModuleType,
[NotNull] IServiceCollection services,
[CanBeNull] Action<AbpApplicationCreationOptions> optionsAction)
{
Check.NotNull(startupModuleType, nameof(startupModuleType));
Check.NotNull(services, nameof(services));
// 设置启动模块。
StartupModuleType = startupModuleType;
Services = services;
// 添加一个空的对象访问器,该访问器的值会在初始化的时候被赋值。
services.TryAddObjectAccessor<IServiceProvider>();
// 调用用户传入的配置委托。
var options = new AbpApplicationCreationOptions(services);
optionsAction?.Invoke(options);
// 注册自己。
services.AddSingleton<IAbpApplication>(this);
services.AddSingleton<IModuleContainer>(this);
// 添加日志等基础设施组件。
services.AddCoreServices();
// 添加核心的 Abp 服务,主要是模块系统相关组件。
services.AddCoreAbpServices(this, options);
// 加载模块,并按照依赖关系排序,依次执行他们的生命周期方法。
Modules = LoadModules(services, options);
}
提示:
这里的对象访问器其实就是一个占位的类型对象,这样方面后面替换其具体实现。例如在上文当中的
IServiceProvider通过ObjectAccessor<T>对象包裹起来,其值是 NULL,但是在后面我们可以根据自己的需要替换其具体的 Value 。
2.3 替换 IoC 容器
再回到之前调用 AddApplication<T>() 传递的委托方法,在其内部我们调用了 UseAutofac() 方法。这个方法很简单,内部就只有三行代码。
这三行代码主要是初始化了一个 AutoFac 的容器构建对象,其次注入 IServiceProviderFactory 和 Abp 的默认实现 AbpAutofacServiceProviderFactory 。
public static void UseAutofac(this AbpApplicationCreationOptions options)
{
var builder = new ContainerBuilder();
options.Services.AddObjectAccessor(builder);
// 这里是实例注册。
options.Services.AddSingleton((IServiceProviderFactory<ContainerBuilder>) new AbpAutofacServiceProviderFactory(builder));
}
这个工厂类的就是在构建 IServiceProvider 的时候使用,即 BuildServiceProviderFromFactory() 方法。该方法内部逻辑很简单,就是从已经注册的服务集合(IServiceCollection)当中获得之前注册的工厂类,通过调用工厂类的 CreateServiceProvider() 方法构建 IServiceProvider,并作为返回值替换掉默认的 IoC 容器。
public static IServiceProvider BuildServiceProviderFromFactory([NotNull] this IServiceCollection services)
{
Check.NotNull(services, nameof(services));
// 遍历已经注册的类型,找到之前注入的工厂类。
foreach (var service in services)
{
var factoryInterface = service.ImplementationInstance?.GetType()
.GetTypeInfo()
.GetInterfaces()
.FirstOrDefault(i => i.GetTypeInfo().IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IServiceProviderFactory<>));
if (factoryInterface == null)
{
continue;
}
// 通过反射调用 IServiceProvider 的构建方法。
var containerBuilderType = factoryInterface.GenericTypeArguments[0];
return (IServiceProvider)typeof(ServiceCollectionCommonExtensions)
.GetTypeInfo()
.GetMethods()
.Single(m => m.Name == nameof(BuildServiceProviderFromFactory) && m.IsGenericMethod)
.MakeGenericMethod(containerBuilderType)
.Invoke(null, new object[] { services, null });
}
return services.BuildServiceProvider();
}
// 这里是另外一个重载方法的定义。
public static IServiceProvider BuildServiceProviderFromFactory<TContainerBuilder>([NotNull] this IServiceCollection services, Action<TContainerBuilder> builderAction = null)
{
Check.NotNull(services, nameof(services));
var serviceProviderFactory = services.GetSingletonInstanceOrNull<IServiceProviderFactory<TContainerBuilder>>();
if (serviceProviderFactory == null)
{
throw new AbpException($"Could not find {typeof(IServiceProviderFactory<TContainerBuilder>).FullName} in {services}.");
}
var builder = serviceProviderFactory.CreateBuilder(services);
builderAction?.Invoke(builder);
return serviceProviderFactory.CreateServiceProvider(builder);
}
2.3 初始化 Abp 框架
这里针对 IApplicationBuilder 的扩展是在模块包 Volo.Abp.AspNetCore 当中的,这里仅讲解 ASP.NET Core Mvc 项目是如何处理的。
public static void InitializeApplication([NotNull] this IApplicationBuilder app)
{
Check.NotNull(app, nameof(app));
// 获取 IApplicationBuilde 的对象访问器,并将其值设置为 app。
app.ApplicationServices.GetRequiredService<ObjectAccessor<IApplicationBuilder>>().Value = app;
// 获得之前在 ConfigureService 注册的 Provider 类型,并调用其初始化方法。
app.ApplicationServices.GetRequiredService<IAbpApplicationWithExternalServiceProvider>().Initialize(app.ApplicationServices);
}
这里可能会疑惑 ObjectAccessor<IApplicationBuilder> 是在什么时候注入的,其实该类型是在 AbpAspNetCoreModule 模块注册的。
public class AbpAspNetCoreModule : AbpModule
{
// ... 其他代码
public override void ConfigureServices(ServiceConfigurationContext context)
{
// ... 其他代码
context.Services.AddObjectAccessor<IApplicationBuilder>();
}
// ... 其他代码
}
接着看初始化方法内部的操作,初始化方法定义是在基类当中,方法名是 InitializeModules() ,在方法内部,通过 IModuleManager 来执行模块的初始化方法。
protected virtual void InitializeModules()
{
using (var scope = ServiceProvider.CreateScope())
{
scope.ServiceProvider
.GetRequiredService<IModuleManager>()
.InitializeModules(new ApplicationInitializationContext(scope.ServiceProvider));
}
}
除了模块的初始化,模块的销毁动作 Abp vNext 好像是没有作处理,你可以挂载 IApplicationLifetime.ApplicationStopping 事件来手动执行模块的销毁方法。
三、总结
总体来说 Abp vNext 的启动流程与之前精简了许多,这是因为在新的框架当中将许多基础组件从核心层移除了,用户可以自由选择自己需要加载的组件。IoC 相关的代码则是通过的 Microsoft Dependency 提供的 IServiceProvider/IServiceCollection 进行操作,没有了之前的 IocManager。
四、点击我跳转到文章目录
[Abp vNext 源码分析] - 1. 框架启动流程分析的更多相关文章
- ThinkPHP5.0源码学习之框架启动流程
ThinkPHP5框架的启动流程图如下: ThinkPHP5的启动流程按照文件分为三步: 1.请求入口(public/index.php) 2.框架启动(thinkphp/start.php) 3.应 ...
- [Abp vNext 源码分析] - 文章目录
一.简要介绍 ABP vNext 是 ABP 框架作者所发起的新项目,截止目前 (2019 年 2 月 18 日) 已经拥有 1400 多个 Star,最新版本号为 v 0.16.0 ,但还属于预览版 ...
- [Abp vNext 源码分析] - 5. DDD 的领域层支持(仓储、实体、值对象)
一.简要介绍 ABP vNext 框架本身就是围绕着 DDD 理念进行设计的,所以在 DDD 里面我们能够见到的实体.仓储.值对象.领域服务,ABP vNext 框架都为我们进行了实现,这些基础设施都 ...
- Abp Vnext源码解析系列文章01---EventBus
一.简介 BP vNext 封装了两种事件总线结构,第一种是 ABP vNext 自己实现的本地事件总线,这种事件总线无法跨项目发布和订阅.第二种则是分布式事件总线,ABP vNext 自己封装了一个 ...
- Cocos2dx源码赏析(1)之启动流程与主循环
Cocos2dx源码赏析(1)之启动流程与主循环 我们知道Cocos2dx是一款开源的跨平台游戏引擎,而学习开源项目一个较实用的办法就是读源码.所谓,"源码之前,了无秘密".而笔者 ...
- Tomcat源码分析之—具体启动流程分析
从Tomcat启动调用栈可知,Bootstrap类的main方法为整个Tomcat的入口,在init初始化Bootstrap类的时候为设置Catalina的工作路径也就是Catalina_HOME信息 ...
- [Abp 源码分析]一、Abp 框架启动流程分析
Abp 不一定仅用于 Asp.Net Core 项目,他也可以在 Console 与 WinFrom 项目当中进行使用,所以关于启动流程可以分为两种,一种是 Asp.Net Core 项目的启动流程, ...
- [Abp vNext 源码分析] - 2. 模块系统的变化
一.简要说明 本篇文章主要分析 Abp vNext 当中的模块系统,从类型构造层面上来看,Abp vNext 当中不再只是单纯的通过 AbpModuleManager 来管理其他的模块,它现在则是 I ...
- [Abp vNext 源码分析] - 6. DDD 的应用层支持 (应用服务)
一.简要介绍 ABP vNext 针对于应用服务层,为我们单独设计了一个模块进行实现,即 Volo.Abp.Ddd.Application 模块. PS:最近博主也是在恶补 DDD 相关的知识,这里推 ...
随机推荐
- 根据NABCD原则完成的案例项目需求分析及其创新方法
<well> Need:在当下的知识经济时代,社会效率在提升,社会竞争强度也在增大,现代社会对人的素质提出了更高的要求,这导致了越来越多的人心理压力增大.well产品就是为了缓解人们心理压 ...
- windows server 2012 R2汉化 -- 玩转Microsoft Azure
Microsoft Azure 试用版小试牛刀 首先需要申请一个账号获得试用权 我这里是1元免费试用, 进入后就可以创建自己的虚拟机及数据库 在这里先说创建的windows server 2012 R ...
- BZOJ_1085_[SCOI2005]骑士精神_IDDFS
BZOJ_1085_[SCOI2005]骑士精神_DFS Description 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位.在任何时候一个骑士都能按照骑 士的走法(它可 ...
- golang 中 string 转换 []byte 的一道笔试题
背景 去面试的时候遇到一道和 string 相关的题目,记录一下用到的知识点.题目如下: s:="123" ps:=&s b:=[]byte(s) pb:=&b s ...
- Android LayoutInflater源码解析:你真的能正确使用吗?
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 好久没写博客了,最近忙着换工作,没时间写,工作刚定下来.稍后有时间会写一下换工作经历.接下来进入本篇主题,本来没想写LayoutInflater的 ...
- 8000个JQuery特效(插件)
各式各样的JQuery场景实现,可供项目使用,请注意插件版本和浏览器适配 下面是下载地址
- Jenkins之Log Parse的使用
在初学使用Jenkins的同学们,应该都遇到过通过bash或者cmd命令执行输出的日志,没办法做到对error, warning等状态的分析和统计.在这里就给他介绍一款实现此功能的插件Log Pars ...
- .NET Core IdentityServer4实战 第一章-入门与API添加客户端凭据
内容:本文带大家使用IdentityServer4进行对API授权保护的基本策略 作者:zara(张子浩) 欢迎分享,但需在文章鲜明处留下原文地址. 本文将要讲述如何使用IdentityServer4 ...
- 8天入门docker系列 —— 第二天 通过一个aspnetcore程序加深对容器的理解
我们知道容器是一个打包了应用和相关依赖的盒子,那怎么去操控这个盒子呢? 这一篇我通过一个简单的aspnetcore程序来加深对盒子的理解,使用之前先 安装一下Docker的环境. 一:Docker的安 ...
- Python库的安装
window下python2.python3安装包的方法 一.在线安装 安装好python.设置好环境变量后,在python安装目录下Script文件夹内会存在pip.exe和easy_install ...