[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
然後我們的 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 框架啟動流程分析