[ABP]浅谈模块系统与 ABP 框架初始化
在 ABP 框架当中所有库以及项目都是以模块的形式存在,所有模块都是继承自AbpModule
这个抽象基类,每个模块都拥有四个生命周期。分别是:
- PreInitialze();
- Initialize();
- PostInitialize():
-
ShutDown();
AddAbp<TStartipModule>()
在初始化 ABP 框架的时候,通过
services.AddAbp<AbpTestMulitPageWebHostModule>
方法将启动模块作为泛型参数传入到 AddAbp 当中。
之后根据传入的启动模块,初始化AbpBootstrapper
,在 AbpBootstrapper 初始化的时候执行拦截器注册等操作。
之后配置 Asp Net Core 相关服务,替换控制器、视图组件、过滤器等默认实现,改用ABP 框架的实现,并且将 Ioc 容器替换为 CastleWindsor。app.UseAbp()
之前的 AddAbp 仅仅是在 ConfigureService 注入服务,紧接着就会在 Configure方法启用 Abp 中间件。
private static void InitializeAbp(IApplicationBuilder app) { var abpBootstrapper = app.ApplicationServices.GetRequiredService<AbpBootstrapper>(); abpBootstrapper.Initialize(); }
可以看到在初始化 ABP 的时候,实际上是从 Ioc 容器中解析出 AbpBootStrapper 调用它的初始化方法。
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; } }
首先注册了日志组件,之后再次注册了 AbpBootStarpper,可能是防止 Ioc 容器没有注册成功吧。
然后调用了 Castle 的 Install 方法,将一些核心组件通过安装器注入到容器当中。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() ); }
下一步解析 AbpBootStrapperConfiguration 初始化所有核心模块的配置项。
之后就是最重要的初始化模块操作了。private void LoadAllModules() { Logger.Debug("Loading Abp modules..."); List<Type> plugInModuleTypes; // 查找所有模块,封装到 List<Type> 容器 var moduleTypes = FindAllModuleTypes(out plugInModuleTypes).Distinct().ToList(); Logger.Debug("Found " + moduleTypes.Count + " ABP modules in total."); // 注册模块到 Ioc 容器 RegisterModules(moduleTypes); CreateModules(moduleTypes, plugInModuleTypes); _modules.EnsureKernelModuleToBeFirst(); _modules.EnsureStartupModuleToBeLast(); SetDependencies(); Logger.DebugFormat("{0} modules loaded.", _modules.Count); }
FindAllModuleTypes
private List<Type> FindAllModuleTypes(out List<Type> plugInModuleTypes) { plugInModuleTypes = new List<Type>(); // 内部根据[DependsOn]特性来,递归获取所有模块 var modules = AbpModule.FindDependedModuleTypesRecursivelyIncludingGivenModule(_modules.StartupModuleType); foreach (var plugInModuleType in _abpPlugInManager.PlugInSources.GetAllModules()) { if (modules.AddIfNotContains(plugInModuleType)) { plugInModuleTypes.Add(plugInModuleType); } } return modules; }
CreateModules
private void CreateModules(ICollection<Type> moduleTypes, List<Type> plugInModuleTypes) { foreach (var moduleType in moduleTypes) { var moduleObject = _iocManager.Resolve(moduleType) as AbpModule; if (moduleObject == null) { throw new AbpInitializationException("This type is not an ABP module: " + moduleType.AssemblyQualifiedName); } moduleObject.IocManager = _iocManager; moduleObject.Configuration = _iocManager.Resolve<IAbpStartupConfiguration>(); // 将模块类型封装到 AbpModule 当中 var moduleInfo = new AbpModuleInfo(moduleType, moduleObject, plugInModuleTypes.Contains(moduleType)); _modules.Add(moduleInfo); // 设置启动模块 if (moduleType == _modules.StartupModuleType) { StartupModule = moduleInfo; } Logger.DebugFormat("Loaded module: " + moduleType.AssemblyQualifiedName); } }
总的来说 CreateModules 的作用就是将之前获取到的模块类型数据再封装为
AbpModuleInfo
对象。在 ModuleInfo 对象内部还包括了这个模块所依赖的模块信息。
构建好所有模块的 ModuleInfo 信息之后,对这个List<ModuleInfo>
进行排序,将启动模块放在最后,将核心模块放在第一位,具体操作可以参考_modules.EnsureKernelModuleToBeFirst();
和_modules.EnsureStartupModuleToBeLast();
这两个方法。这么做是因为要确保最核心的模块第一位初始化,然后再依次初始化他的子模块。因为是启动模块,所以他是这个依赖树的最低端,留在最后初始化。SetDependencies
遍历
List<ModuleType>
设置每个模块的依赖模块。回到最开始的地方,这里仅仅是初始化模块,之后调用了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()); }
启动模块的时候,先按照依赖项来排序,顺序是 Kernal->Module1->Module->2->StartModule。之后从 PreInitialize->Initialize->PostInitialize 这样遍历执行。
执行完之后所有模块就已经初始化完成了。
这里可以看到并没有 ShutDown 方法执行,ShutDown 执行的时机是在 AbpBootStrapper 被释放的时候,进行调用。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."); }
不过执行 ShutDown 方法的时候,会将模块列表反转,按照 Start->Module2->Module1->Kernal 这样来关闭。
模块是 ABP 框架的基础单元,在 ABP 的实现当中模块大部分被当做功能库的形式存在。如果你要使用 ABP 框架的话,必须要定义一个启动模块,不然其他功能是无法正常进行初始化的。AbpModule
public abstract class AbpModule { /// <summary> /// Gets a reference to the IOC manager. /// </summary> protected internal IIocManager IocManager { get; internal set; } /// <summary> /// Gets a reference to the ABP configuration. /// </summary> protected internal IAbpStartupConfiguration Configuration { get; internal set; } /// <summary> /// Gets or sets the logger. /// </summary> public ILogger Logger { get; set; } protected AbpModule() { Logger = NullLogger.Instance; } /// <summary> /// This is the first event called on application startup. /// Codes can be placed here to run before dependency injection registrations. /// </summary> public virtual void PreInitialize() { } /// <summary> /// This method is used to register dependencies for this module. /// </summary> public virtual void Initialize() { } /// <summary> /// This method is called lastly on application startup. /// </summary> public virtual void PostInitialize() { } /// <summary> /// This method is called when the application is being shutdown. /// </summary> public virtual void Shutdown() { } public virtual Assembly[] GetAdditionalAssemblies() { return new Assembly[0]; } /// <summary> /// Checks if given type is an Abp module class. /// </summary> /// <param name="type">Type to check</param> public static bool IsAbpModule(Type type) { var typeInfo = type.GetTypeInfo(); return typeInfo.IsClass && !typeInfo.IsAbstract && !typeInfo.IsGenericType && typeof(AbpModule).IsAssignableFrom(type); } /// <summary> /// Finds direct depended modules of a module (excluding given module). /// </summary> public static List<Type> FindDependedModuleTypes(Type moduleType) { if (!IsAbpModule(moduleType)) { throw new AbpInitializationException("This type is not an ABP module: " + moduleType.AssemblyQualifiedName); } var list = new List<Type>(); if (moduleType.GetTypeInfo().IsDefined(typeof(DependsOnAttribute), true)) { var dependsOnAttributes = moduleType.GetTypeInfo().GetCustomAttributes(typeof(DependsOnAttribute), true).Cast<DependsOnAttribute>(); foreach (var dependsOnAttribute in dependsOnAttributes) { foreach (var dependedModuleType in dependsOnAttribute.DependedModuleTypes) { list.Add(dependedModuleType); } } } return list; } public static List<Type> FindDependedModuleTypesRecursivelyIncludingGivenModule(Type moduleType) { var list = new List<Type>(); AddModuleAndDependenciesRecursively(list, moduleType); list.AddIfNotContains(typeof(AbpKernelModule)); return list; } private static void AddModuleAndDependenciesRecursively(List<Type> modules, Type module) { if (!IsAbpModule(module)) { throw new AbpInitializationException("This type is not an ABP module: " + module.AssemblyQualifiedName); } if (modules.Contains(module)) { return; } modules.Add(module); var dependedModules = FindDependedModuleTypes(module); foreach (var dependedModule in dependedModules) { AddModuleAndDependenciesRecursively(modules, dependedModule); } } }
在 Abp 模块当中会为你注入一些必须的设施,比如 Ioc 容器,模块配置集合,日志记录器等。
如果想知道模块如何编写,可以参考 ABP 原有的功能模块实现。
上一篇: vue使用axios实现文件上传进度的实时更新详解
下一篇: 浅谈ASP.NET配置文件加密