[Abp 源码分析]一、Abp 框架启动流程分析
Abp 不一定仅用于 Asp.Net Core 项目,他也可以在 Console 与 WinFrom 项目当中进行使用,所以关于启动流程可以分为两种,一种是 Asp.Net Core 项目的启动流程,另外则是 ConsoleApplication/WinFrom 项目的启动流程,在这里我则是通过 Asp.Net Core 项目的启动流程来分析,但是他们的核心都是 AbpBootstrapper 这个类。
本文章基于 Abp 框架的最新版本 v3.7.2 。
一、Abp 的入口点
1.1 添加服务与启用中间件
要在 Asp.Net Core 项目当中使用 Abp 框架的话,第一步当然是先添加 Abp.AspNetCore
库啦,之后在我们 Startup
类的 ConfigureAbpService(IServiceCollection services)
方法里面使用 AddAbp<TStartupModule>
。比如像这样:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
return services.AddAbp<HKAbpDemoHostModule>();
}
注意,这里我们 ConfigureService 返回类型变成了 IServiceProvider
,这是因为在 AddAbp
方法内部替换了 Ioc 容器,变成了 CastleWindsor,后面会接着分析的。
然后我们的 Configure(IApplicationBuilder app, IHostingEnvironment env)
方法里面也会有如下代码:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAbp();
}
1.2 配置服务
之后来到 AddAbp 方法内部,文件目录位置如下:
Abp\src\Abp.AspNetCore\AspNetCore\AbpServiceCollectionExtensions.cs
public static IServiceProvider AddAbp<TStartupModule>(this IServiceCollection services, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
where TStartupModule : AbpModule
{
var abpBootstrapper = AddAbpBootstrapper<TStartupModule>(services, optionsAction);
ConfigureAspNetCore(services, abpBootstrapper.IocManager);
return WindsorRegistrationHelper.CreateServiceProvider(abpBootstrapper.IocManager.IocContainer, services);
}
1.2.1 注入 AbpBootstrapper
在 AddAbpBootstrapper()
方法内部将使用 AbpBootstrapper
的 Create
方法创建一个新的 AbpBootstrapper
实例。并且通过 IServiceCollection
将其注入到 Ioc 容器当中。
1.2.2 配置 AspNetCore 相关参数
private static void ConfigureAspNetCore(IServiceCollection services, IIocResolver iocResolver)
{
//See https://github.com/aspnet/Mvc/issues/3936 to know why we added these services.
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();
//Use DI to create controllers
services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
//Use DI to create view components
services.Replace(ServiceDescriptor.Singleton<IViewComponentActivator, ServiceBasedViewComponentActivator>());
//Change anti forgery filters (to work proper with non-browser clients)
services.Replace(ServiceDescriptor.Transient<AutoValidateAntiforgeryTokenAuthorizationFilter, AbpAutoValidateAntiforgeryTokenAuthorizationFilter>());
services.Replace(ServiceDescriptor.Transient<ValidateAntiforgeryTokenAuthorizationFilter, AbpValidateAntiforgeryTokenAuthorizationFilter>());
//Add feature providers
var partManager = services.GetSingletonServiceOrNull<ApplicationPartManager>();
partManager?.FeatureProviders.Add(new AbpAppServiceControllerFeatureProvider(iocResolver));
//Configure JSON serializer
services.Configure<MvcJsonOptions>(jsonOptions =>
{
jsonOptions.SerializerSettings.ContractResolver = new AbpContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
};
});
//Configure MVC
services.Configure<MvcOptions>(mvcOptions =>
{
mvcOptions.AddAbp(services);
});
//Configure Razor
services.Insert(0,
ServiceDescriptor.Singleton<IConfigureOptions<RazorViewEngineOptions>>(
new ConfigureOptions<RazorViewEngineOptions>(
(options) =>
{
options.FileProviders.Add(new EmbeddedResourceViewFileProvider(iocResolver));
}
)
)
);
}
其方法内部做了比较多的工作,主要是配置与 Asp.Net Core 相关的一些配置,比如替换一些默认的服务呀这些。这里重点注意一下这段代码:
mvcOptions.AddAbp(services);
这是 Abp 所写的一个静态方法,这里面就是添加 Abp 内部所实现的过滤器的:
internal static class AbpMvcOptionsExtensions
{
public static void AddAbp(this MvcOptions options, IServiceCollection services)
{
AddConventions(options, services);
AddFilters(options);
AddModelBinders(options);
}
private static void AddConventions(MvcOptions options, IServiceCollection services)
{
options.Conventions.Add(new AbpAppServiceConvention(services));
}
private static void AddFilters(MvcOptions options)
{
options.Filters.AddService(typeof(AbpAuthorizationFilter));
options.Filters.AddService(typeof(AbpAuditActionFilter));
options.Filters.AddService(typeof(AbpValidationActionFilter));
options.Filters.AddService(typeof(AbpUowActionFilter));
options.Filters.AddService(typeof(AbpExceptionFilter));
options.Filters.AddService(typeof(AbpResultFilter));
}
private static void AddModelBinders(MvcOptions options)
{
options.ModelBinderProviders.Insert(0, new AbpDateTimeModelBinderProvider());
}
}
1.2.3 替换 Ioc 容器
最后一句话即:
return WindsorRegistrationHelper.CreateServiceProvider(abpBootstrapper.IocManager.IocContainer, services);
就是替换掉了 Asp.Net Core 默认的 Ioc 容器,不是 IServiceCollection
了,而是 CastleWindsor 的 IocContainer
。
1.3 启用服务
在 Startup
的 Configure
方法当中我们使用了 app.UseAbp()
来启用 Abp 框架,他的定义可以在以下位置找到:
Abp\src\Abp.AspNetCore\AspNetCore\AbpApplicationBuilderExtensions.cs
public static void UseAbp([NotNull] this IApplicationBuilder app, Action<AbpApplicationBuilderOptions> optionsAction)
{
Check.NotNull(app, nameof(app));
var options = new AbpApplicationBuilderOptions();
optionsAction?.Invoke(options);
if (options.UseCastleLoggerFactory)
{
app.UseCastleLoggerFactory();
}
InitializeAbp(app);
if (options.UseAbpRequestLocalization)
{
//TODO: This should be added later than authorization middleware!
app.UseAbpRequestLocalization();
}
if (options.UseSecurityHeaders)
{
app.UseAbpSecurityHeaders();
}
}
它可以允许用户自己配置一些相关的参数,并且在 InitializeAbp(app)
里面进行了初始化操作。
跳转到 InitializeAbp(app)
定义的地方:
private static void InitializeAbp(IApplicationBuilder app)
{
var abpBootstrapper = app.ApplicationServices.GetRequiredService<AbpBootstrapper>();
abpBootstrapper.Initialize();
var applicationLifetime = app.ApplicationServices.GetService<IApplicationLifetime>();
applicationLifetime.ApplicationStopping.Register(() => abpBootstrapper.Dispose());
}
这里通过 IServiceProvider
获取到之前 AddAbp
注入的 AbpBootstrapper
对象,并且调用其初始化方法。
这里还注册了一个生命周期事件,当程序停止的时候调用 AbpBootstrapper
的销毁方法。
二、Abp 框架初始化
整个 Abp 框架启动之后的初始化操作都存放在 AbpBootstrapper
当中,包括框架内部的各种基础设施的注入与所有模块加载操作,在上文可以看到是调用的 Initialize()
方法来进行初始化。
public virtual void Initialize()
{
ResolveLogger();
try
{
RegisterBootstrapper();
IocManager.IocContainer.Install(new AbpCoreInstaller());
IocManager.Resolve<AbpPlugInManager>().PlugInSources.AddRange(PlugInSources);
IocManager.Resolve<AbpStartupConfiguration>().Initialize();
_moduleManager = IocManager.Resolve<AbpModuleManager>();
_moduleManager.Initialize(StartupModule);
_moduleManager.StartModules();
}
catch (Exception ex)
{
_logger.Fatal(ex.ToString(), ex);
throw;
}
}
1.1 注入基础设施
基础设施的注入是通过 Windsor 的 IocContainer
来注册所有基础设施的,可以看到他使用 Install()
方法来注册。我们可以看一下 AbpCoreInstaller
的定义。
internal class AbpCoreInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IUnitOfWorkDefaultOptions, UnitOfWorkDefaultOptions>().ImplementedBy<UnitOfWorkDefaultOptions>().LifestyleSingleton(),
Component.For<INavigationConfiguration, NavigationConfiguration>().ImplementedBy<NavigationConfiguration>().LifestyleSingleton(),
Component.For<ILocalizationConfiguration, LocalizationConfiguration>().ImplementedBy<LocalizationConfiguration>().LifestyleSingleton(),
Component.For<IAuthorizationConfiguration, AuthorizationConfiguration>().ImplementedBy<AuthorizationConfiguration>().LifestyleSingleton(),
Component.For<IValidationConfiguration, ValidationConfiguration>().ImplementedBy<ValidationConfiguration>().LifestyleSingleton(),
Component.For<IFeatureConfiguration, FeatureConfiguration>().ImplementedBy<FeatureConfiguration>().LifestyleSingleton(),
Component.For<ISettingsConfiguration, SettingsConfiguration>().ImplementedBy<SettingsConfiguration>().LifestyleSingleton(),
Component.For<IModuleConfigurations, ModuleConfigurations>().ImplementedBy<ModuleConfigurations>().LifestyleSingleton(),
Component.For<IEventBusConfiguration, EventBusConfiguration>().ImplementedBy<EventBusConfiguration>().LifestyleSingleton(),
Component.For<IMultiTenancyConfig, MultiTenancyConfig>().ImplementedBy<MultiTenancyConfig>().LifestyleSingleton(),
Component.For<ICachingConfiguration, CachingConfiguration>().ImplementedBy<CachingConfiguration>().LifestyleSingleton(),
Component.For<IAuditingConfiguration, AuditingConfiguration>().ImplementedBy<AuditingConfiguration>().LifestyleSingleton(),
Component.For<IBackgroundJobConfiguration, BackgroundJobConfiguration>().ImplementedBy<BackgroundJobConfiguration>().LifestyleSingleton(),
Component.For<INotificationConfiguration, NotificationConfiguration>().ImplementedBy<NotificationConfiguration>().LifestyleSingleton(),
Component.For<IEmbeddedResourcesConfiguration, EmbeddedResourcesConfiguration>().ImplementedBy<EmbeddedResourcesConfiguration>().LifestyleSingleton(),
Component.For<IAbpStartupConfiguration, AbpStartupConfiguration>().ImplementedBy<AbpStartupConfiguration>().LifestyleSingleton(),
Component.For<IEntityHistoryConfiguration, EntityHistoryConfiguration>().ImplementedBy<EntityHistoryConfiguration>().LifestyleSingleton(),
Component.For<ITypeFinder, TypeFinder>().ImplementedBy<TypeFinder>().LifestyleSingleton(),
Component.For<IAbpPlugInManager, AbpPlugInManager>().ImplementedBy<AbpPlugInManager>().LifestyleSingleton(),
Component.For<IAbpModuleManager, AbpModuleManager>().ImplementedBy<AbpModuleManager>().LifestyleSingleton(),
Component.For<IAssemblyFinder, AbpAssemblyFinder>().ImplementedBy<AbpAssemblyFinder>().LifestyleSingleton(),
Component.For<ILocalizationManager, LocalizationManager>().ImplementedBy<LocalizationManager>().LifestyleSingleton()
);
}
}
可以看到他注入了很多配置项,比如说缓存,权限配置,还有模块管理器之类的,这些我会在以后的文章当中进行具体解释。
他继承了 IWindsorInstaller
接口,这个是 CastleWindsor 所提供的,专门用于某一些功能的类型进行统一注册,除了 AbpCoreInstaller
其实还有 EventBusInstaller
这个是用于注册事件总线相关类型的,后面再讲。
1.2 模块初始化
1.2.1 加载模块
_moduleManager = IocManager.Resolve<AbpModuleManager>();
_moduleManager.Initialize(StartupModule);
通过 ModuleManager.Initialize()
来加载所有模块。
public virtual void Initialize(Type startupModule)
{
_modules = new AbpModuleCollection(startupModule);
LoadAllModules();
}
他的内部首先初始化了一个集合,这是 Abp 自己定义的,它的本质就是一个集合,只不过提供了一些诸如根据依赖关系来排序的操作,下面的 LoadAllModules()
则是真正的加载模块了。
private void LoadAllModules()
{
Logger.Debug("Loading Abp modules...");
List<Type> plugInModuleTypes;
var moduleTypes = FindAllModuleTypes(out plugInModuleTypes).Distinct().ToList();
Logger.Debug("Found " + moduleTypes.Count + " ABP modules in total.");
RegisterModules(moduleTypes);
CreateModules(moduleTypes, plugInModuleTypes);
_modules.EnsureKernelModuleToBeFirst();
_modules.EnsureStartupModuleToBeLast();
SetDependencies();
Logger.DebugFormat("{0} modules loaded.", _modules.Count);
}
这里很简单了,首先在 FindAllModuleTypes()
方法内部通过启动模块上面的 [DependsOn]
标签来从最外层加载插件形式的模块与内部模块。
之后将通过 RegisterModules
所有模块单例注入到 Ioc 容器内部,而 CreateModules()
方法则为每个模块来配置里面的一些公有属性,并且将其包装到 AbpModuleInfo
里面。
你可能会有疑问,已经有了模块的类型,为什么还要一层包装。
因为为了确保模块按正确的顺序来进行加载,所以需要拥有每个模块的详细信息,主要是依赖信息,正确的顺序应该是核心模块在最里层,而启动模块应该是在最底层的。所以在后面他还调用了 AbpModuleManager
的 EnsureKernelModuleToBeFirst()
方法与 EnsureStartupModuleToBeLast()
方法,以确保正确的加载顺序。
而 SetDependencies()
方法则是来为每一个 ModuleInfo
配置正确的依赖关系。
1.2.2 初始化模块
所有模块的依赖关系与实例都已经被存放到了 AbpModuleCollection
里面了,下面就来启动这些模块了,启动模块的方法则是 StartModules()
。
public virtual void StartModules()
{
var sortedModules = _modules.GetSortedModuleListByDependency();
sortedModules.ForEach(module => module.Instance.PreInitialize());
sortedModules.ForEach(module => module.Instance.Initialize());
sortedModules.ForEach(module => module.Instance.PostInitialize());
}
可以看到这里的 GetSortedModuleListByDependency()
方法就是根据依赖关系来进行最后的排序,以确保模块加载顺序的正确。
后面则没什么了,使用 ForEach 来按照正常的生命周期来调用所有模块的几个生命周期方法。
可以看到这里没有调用 ShutDown()
方法是因为这个方法只有当程序结束的时候才会调用,他被单独包装到了一个方法当中。
public virtual void ShutdownModules()
{
Logger.Debug("Shutting down has been started");
var sortedModules = _modules.GetSortedModuleListByDependency();
sortedModules.Reverse();
sortedModules.ForEach(sm => sm.Instance.Shutdown());
Logger.Debug("Shutting down completed.");
}
而 ShutdownModules()
则是在我们的 AbpBootStrapper
的 Dispose 时候才被调用,他什么时候被销毁的呢?就是我们最开始 app.UseAbp()
的时候与 IApplicationLifetime
的 ApplicationStopping
绑定的。
三、结束语
本篇文章主要将了 ABP 框架的一个基本启动流程,很简单并不深入,后续会继续发文,因为之前看的是 HK Zhang 的文章,但是他是基于很早之前的版本,在工作中也经常针对 Abp 源码进行一些扩展和更改,所以想写一些这方面的文章,后续也会在分析当中贴上具体应用 Abp 框架时候的坑。
四、点此跳转到总目录
[Abp 源码分析]一、Abp 框架启动流程分析的更多相关文章
- Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Job Manager 启动
Job Manager 启动 https://t.zsxq.com/AurR3rN 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac ...
- Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Task Manager 启动
Task Manager 启动 https://t.zsxq.com/qjEUFau 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Ma ...
- Flink 源码解析 —— Standalone session 模式启动流程
Standalone session 模式启动流程 https://t.zsxq.com/EemAEIi 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0 ...
- Caddy源码阅读(二)启动流程与 Event 事件通知
Caddy源码阅读(二)启动流程与 Event 事件通知 Preface Caddy 是 Go 语言构建的轻量配置化服务器.https://github.com/caddyserver/caddy C ...
- [Abp vNext 源码分析] - 1. 框架启动流程分析
一.简要说明 本篇文章主要剖析与讲解 Abp vNext 在 Web API 项目下的启动流程,让大家了解整个 Abp vNext 框架是如何运作的.总的来说 ,Abp vNext 比起 ABP 框架 ...
- 头秃了,二十三张图带你从源码了解Spring Boot 的启动流程~
持续原创输出,点击上方蓝字关注我 目录 前言 源码版本 从哪入手? 源码如何切分? 如何创建SpringApplication? 设置应用类型 设置初始化器(Initializer) 设置监听器(Li ...
- Tomcat8源码笔记(九)组件StandardContext启动流程--未完待续
StandardContext代表的是webapps下项目,一个项目就是一个StandardContext,作为Tomcat组件的一部分,就会实现Lifecycle接口,被Tomcat管理着生命周期, ...
- springBoot高级:自动配置分析,事件监听,启动流程分析,监控,部署
知识点梳理 课堂讲义 02-SpringBoot自动配置-@Conditional使用 Condition是Spring4.0后引入的条件化配置接口,通过实现Condition接口可以完成有条件的加载 ...
- twitter storm源码走读之1 -- nimbus启动场景分析
欢迎转载,转载时请注明作者徽沪一郎及出处,谢谢. 本文详细介绍了twitter storm中的nimbus节点的启动场景,分析nimbus是如何一步步实现定义于storm.thrift中的servic ...
随机推荐
- 杂记:Python 两坑
近日写代码又遇到两个 Python 的坑,觉得值得记录. 递归传参问题 Python 里传参的实现是 assignment,但由于 Python 里都是对象,除了几个基本类型,assignment 基 ...
- 使用chrome-devtools定位元素
2.1使用chrome-devtools 这是谷歌提供的一个移动端Web开发调试工具,通过它我们可以调试手机页面.通过调试我们可以看到wap页面的源码,从而进行元素的定位.那么这个工具该如何使用呢?首 ...
- 在ideaUI中建立maven项目
1.新建New 注意:第一次创建maven项目需要在有网情况下进行 2.可以看见我们建立的项目了
- Tomcat映射路径
打开tomcat安装包,在config目录下修改server.xml文件: 在<Host>标签中添加: <Context path="" docBase=&quo ...
- .net core Razor视图的TagHelper使用方法介绍
TagHelper TagHelper是ASP.NET 5的一个新特性.也许在你还没有听说过它的时候, 它已经在技术人员之间引起了大量讨论,甚至有一部分称它为服务器控件的回归.实际上它只不过是一个简化 ...
- ISP PIPLINE (九_1) Denoise 之 space domain denoise
1.空间域噪声类型 1.gauss+possion 2.椒盐噪声(dpc处理已经处理了) 去除空域噪声有哪些方法? 空域噪声一般的思想是对某pixel邻域的pixels进行加权平均. 比如 1.高斯降 ...
- [OC] 各型号iPhone的屏幕分辨率与开发使用的数值
- HFS 轻量化 的文件服务器
国外的工具 国内的工具
- C语言中free()函数释放struct结构体中的规律
并不是什么新鲜的事情,不过值得注意.首先我们知道,在使用struct来定义并声明一个变量时,将会自动划分出一个连续的储存空间(虽然根据某些对齐原则会出现内存间隙,但是大体上来说还是连续的)这一块连续空 ...
- xampp访问phpmyadmin访问不了
我的xampp版本是xampp-linux-x64-5.6.15-2-installer.run, 浏览器输入“我的ip/phpmyadmin”出现如下问题: Access forbidden! Ne ...