本篇作為我ABP介紹的第三篇文章,這次想講下模塊的,ABP文檔已經有模塊這方面的介紹,但是它只講到如何使用模塊,我想詳細講解下它模塊的設計思路。
ABP 框架提供了創建和組裝模塊的基礎,一個模塊能夠依賴於另一個模塊。在通常情況 下,一個程序集就可以看成是一個模塊。在 ABP 框架中,一個模塊通過一個類來定義,而這 個類要繼承自 AbpModule。
其實它的設計思路很簡單:
1、加載bin目錄下的所有dll
public class WebAssemblyFinder : IAssemblyFinder { /// <summary> /// This return all assemblies in bin folder of the web application. /// </summary> /// <returns>List of assemblies</returns> public List<Assembly> GetAllAssemblies() { var assembliesInBinFolder = new List<Assembly>(); var allReferencedAssemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToList(); var dllFiles = Directory.GetFiles(HttpRuntime.AppDomainAppPath + "bin\\", "*.dll", SearchOption.TopDirectoryOnly).ToList(); foreach (string dllFile in dllFiles) { var locatedAssembly = allReferencedAssemblies.FirstOrDefault(asm => AssemblyName.ReferenceMatchesDefinition(asm.GetName(), AssemblyName.GetAssemblyName(dllFile))); if (locatedAssembly != null) { assembliesInBinFolder.Add(locatedAssembly); } } return assembliesInBinFolder; } }
2、循環判斷獲取所有與AbpModule的Types有關
public static bool IsAbpModule(Type type) { return type.IsClass && !type.IsAbstract && typeof(AbpModule).IsAssignableFrom(type); }
並遞歸獲取沒有只在所有的DependsOnAttribute,把他們填在到modules集合中(請詳細看AbpModule.FindDependedModuleTypes方法)
private static ICollection<Type> AddMissingDependedModules(ICollection<Type> allModules) { var initialModules = allModules.ToList(); foreach (var module in initialModules) { FillDependedModules(module, allModules); } return allModules; } private static void FillDependedModules(Type module, ICollection<Type> allModules) { foreach (var dependedModule in AbpModule.FindDependedModuleTypes(module)) { if (!allModules.Contains(dependedModule)) { allModules.Add(dependedModule); FillDependedModules(dependedModule, allModules); } } }
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.IsDefined(typeof(DependsOnAttribute), true)) { var dependsOnAttributes = moduleType.GetCustomAttributes(typeof(DependsOnAttribute), true).Cast<DependsOnAttribute>(); foreach (var dependsOnAttribute in dependsOnAttributes) { foreach (var dependedModuleType in dependsOnAttribute.DependedModuleTypes) { list.Add(dependedModuleType); } } } return list; }
所有關於模塊的重要代碼都在AbpModuleManager中,在上面我們已經加載了所有的模塊的類型,那么ABP到底有多少個Modules呢
在我下載的Demo中包含了十三個Module,都繼承字AbpModule類
3、既然我得到了所有的moduleTypes了,那么我就通過Castle Windsor循環注冊了,並反轉
private void LoadAll() { Logger.Debug("Loading Abp modules..."); //通過bin加載所有的module集合 var moduleTypes = AddMissingDependedModules(_moduleFinder.FindAll()); Logger.Debug("Found " + moduleTypes.Count + " ABP modules in total."); //通過castle windsor注冊所有的模塊 Register to IOC container. foreach (var moduleType in moduleTypes) { if (!AbpModule.IsAbpModule(moduleType)) { throw new AbpInitializationException("This type is not an ABP module: " + moduleType.AssemblyQualifiedName); } if (!_iocManager.IsRegistered(moduleType)) { _iocManager.Register(moduleType); } } //模塊反轉並添加到_modules中 Add to module collection foreach (var moduleType in moduleTypes) { var moduleObject = (AbpModule)_iocManager.Resolve(moduleType); moduleObject.IocManager = _iocManager; moduleObject.Configuration = _iocManager.Resolve<IAbpStartupConfiguration>(); _modules.Add(new AbpModuleInfo(moduleObject)); Logger.DebugFormat("Loaded module: " + moduleType.AssemblyQualifiedName); } //確保AbpKernelModule是_modules中第一個module,AbpKernelModule must be the first module var startupModuleIndex = _modules.FindIndex(m => m.Type == typeof(AbpKernelModule)); if (startupModuleIndex > 0) { var startupModule = _modules[startupModuleIndex]; _modules.RemoveAt(startupModuleIndex); _modules.Insert(0, startupModule); } SetDependencies(); Logger.DebugFormat("{0} modules loaded.", _modules.Count); }
上面代碼有注釋,詳細請看注釋
4、就是初始化所有模塊的事件,早AbpModule中作者定義了三個事件,在實踐應用在我們會依次執行下面三個方法
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() { }
在一個應用中,ABP 框架調用了 Module 模塊的一些指定的方法來進行啟動和關閉模塊的 操作。我們可以重載這些方法來完成我們自己的任務。 ABP 框架通過依賴關系的順序來調用這些方法,
假如:模塊 A 依賴於模塊 B,那么模塊 B 要在模塊 A 之前初始化,模塊啟動的方法順序如下:
1) PreInitialize-B
2) PreInitialize-A
3) Initialize-B
4) Initialize-A
5) PostInitialize-B
6) PostInitialize-A
那么我們是怎么執行上面的方法的呢,方案在AbpModuleManager的InitializeModules方法中
public virtual void InitializeModules() { LoadAll(); var sortedModules = _modules.GetSortedModuleListByDependency(); sortedModules.ForEach(module => module.Instance.PreInitialize()); sortedModules.ForEach(module => module.Instance.Initialize()); sortedModules.ForEach(module => module.Instance.PostInitialize()); }
那么我們在自定義模塊的時候是要重寫上面三個方法的,有點像管道的事件,我們會依次執行這些事件,現在我們隨便看個AbpEntityFrameworkModule模塊它重寫了其中兩個
public override void PreInitialize() { IocManager.AddConventionalRegistrar(new EntityFrameworkConventionalRegisterer()); } public override void Initialize() { IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); IocManager.IocContainer.Register( Component.For(typeof (IDbContextProvider<>)) .ImplementedBy(typeof (UnitOfWorkDbContextProvider<>)) .LifestyleTransient() ); RegisterGenericRepositories(); }
一般的我們都要在Initialize方法中加上這句IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); 也就是注冊當前程序集,IOC初始化用的哦,好了,至此終於把ABP模塊的思路講完啦,大家可以配合ABP的文檔的相關章節進行研究,希望對初學者有幫助。
參考文章:
https://github.com/ABPFrameWorkGroup/AbpDocument2Chinese