Abp 不必定僅用於 Asp.Net Core 項目,他也能夠在 Console 與 WinFrom 項目當中進行使用,因此關於啓動流程能夠分爲兩種,一種是 Asp.Net Core 項目的啓動流程,另外則是 ConsoleApplication/WinFrom 項目的啓動流程,在這裏我則是經過 Asp.Net Core 項目的啓動流程來分析,可是他們的核心都是 AbpBootstrapper 這個類。html
本文章基於 Abp 框架的最新版本 v3.7.2 。git
要在 Asp.Net Core 項目當中使用 Abp 框架的話,第一步固然是先添加 Abp.AspNetCore
庫啦,以後在咱們 Startup
類的 ConfigureAbpService(IServiceCollection services)
方法裏面使用 AddAbp<TStartupModule>
。好比像這樣:github
public IServiceProvider ConfigureServices(IServiceCollection services) { return services.AddAbp<HKAbpDemoHostModule>(); }
注意,這裏咱們 ConfigureService 返回類型變成了 IServiceProvider
,這是由於在 AddAbp
方法內部替換了 Ioc 容器,變成了 CastleWindsor,後面會接着分析的。shell
而後咱們的 Configure(IApplicationBuilder app, IHostingEnvironment env)
方法裏面也會有以下代碼:json
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseAbp(); }
以後來到 AddAbp 方法內部,文件目錄位置以下:bootstrap
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); }
在 AddAbpBootstrapper()
方法內部將使用 AbpBootstrapper
的 Create
方法建立一個新的 AbpBootstrapper
實例。而且經過 IServiceCollection
將其注入到 Ioc 容器當中。緩存
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 相關的一些配置,好比替換一些默認的服務呀這些。這裏重點注意一下這段代碼:mvc
mvcOptions.AddAbp(services);
這是 Abp 所寫的一個靜態方法,這裏面就是添加 Abp 內部所實現的過濾器的:app
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()); } }
最後一句話即:框架
return WindsorRegistrationHelper.CreateServiceProvider(abpBootstrapper.IocManager.IocContainer, services);
就是替換掉了 Asp.Net Core 默認的 Ioc 容器,不是 IServiceCollection
了,而是 CastleWindsor 的 IocContainer
。
在 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 框架啓動以後的初始化操做都存放在 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; } }
基礎設施的注入是經過 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
這個是用於註冊事件總線相關類型的,後面再講。
_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
配置正確的依賴關係。
全部模塊的依賴關係與實例都已經被存放到了 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 框架時候的坑。