浅谈模块系统与 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 原有的功能模块实现。