zoukankan      html  css  js  c++  java
  • [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 方法内部,文件目录位置如下:

    AbpsrcAbp.AspNetCoreAspNetCoreAbpServiceCollectionExtensions.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() 方法内部将使用 AbpBootstrapperCreate 方法创建一个新的 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 启用服务

    StartupConfigure 方法当中我们使用了 app.UseAbp() 来启用 Abp 框架,他的定义可以在以下位置找到:

    AbpsrcAbp.AspNetCoreAspNetCoreAbpApplicationBuilderExtensions.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 里面。

    你可能会有疑问,已经有了模块的类型,为什么还要一层包装。

    因为为了确保模块按正确的顺序来进行加载,所以需要拥有每个模块的详细信息,主要是依赖信息,正确的顺序应该是核心模块在最里层,而启动模块应该是在最底层的。所以在后面他还调用了 AbpModuleManagerEnsureKernelModuleToBeFirst() 方法与 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() 的时候与 IApplicationLifetimeApplicationStopping 绑定的。

    三、结束语

    本篇文章主要将了 ABP 框架的一个基本启动流程,很简单并不深入,后续会继续发文,因为之前看的是 HK Zhang 的文章,但是他是基于很早之前的版本,在工作中也经常针对 Abp 源码进行一些扩展和更改,所以想写一些这方面的文章,后续也会在分析当中贴上具体应用 Abp 框架时候的坑。

    四、点此跳转到总目录

  • 相关阅读:
    SQLAlchemy
    Redis
    Django框架详解
    nginx负载均衡(反向代理)
    Python 字典 fromkeys()方法
    docker容器的使用
    keepalived原理
    学习区块链必读的6本书
    MATLAB基础入门笔记
    思念是一种美丽的孤独
  • 原文地址:https://www.cnblogs.com/myzony/p/9186838.html
Copyright © 2011-2022 走看看