ABP框架 - 模塊系統html
ABP框架 - 啓動配置web
基於Abp模塊化、插件化的設計,開發人員能夠將自定義的功能以模塊的形式集成到項目中。一般地,一個程序集做爲一個模塊。若是你的應用是多個程序集,建議爲每一個程序集定義一個模塊。源碼分析
插件:post
1. 擴展的HttpApplication對象(在Abp.Web項目中AbpWebApplication<TStartupModule> : HttpApplication)中有AbpBootstrapper成員this
AbpWebApplication的Application_Start方法:url
protected virtual void Application_Start(object sender, EventArgs e) { ThreadCultureSanitizer.Sanitize(); AbpBootstrapper.Initialize(); _webLocalizationConfiguration = AbpBootstrapper.IocManager.Resolve<IAbpWebLocalizationConfiguration>(); }
項目的Global文件中
public class MvcApplication : AbpWebApplication<HKWEBWebModule> { protected override void Application_Start(object sender, EventArgs e) { AbpBootstrapper.IocManager.IocContainer.AddFacility<LoggingFacility>( f => f.UseAbpLog4Net().WithConfig("log4net.config") ); //添加插件 AbpBootstrapper.PlugInSources.AddFolder(@"C:\MyPlugIns"); AbpBootstrapper.PlugInSources.AddTypeList(typeof(MyPlugInModule)); base.Application_Start(sender, e); } }
AbpBootstrapper的Initialize方法
public virtual void Initialize() { //實例化_logger ResolveLogger(); try { //把Bootstrapper類自身加到容器裏 RegisterBootstrapper(); IocManager.IocContainer.Install(new CoreInstaller()); //將附加的插件加入隊列 IocManager.Resolve<PlugInManager>().PlugInSources.AddRange(PlugInSources); //StartupConfiguration.Modules,Settings,ServiceReplaceActions等 IocManager.Resolve<StartupConfiguration>().Initialize(); _moduleManager = IocManager.Resolve<ModuleManager>(); //加載全部Module _moduleManager.Initialize(StartupModule); //對這些Module排序,以後依次執行全部模塊的PreInitialize,Initialize,PostInitialize _moduleManager.StartModules(); } catch (Exception ex) { _logger.Fatal(ex.ToString(), ex); throw; } }
模塊管理器的Initialize方法會加載全部依賴的模塊,並經過模塊類型上的Dependon屬性按照依賴關係對它們進行順序,同時也會加載AbpBootstrapper.PlugInSources中添加的插件(插件的添加 目前提供了兩種實現)。
AbpBootstrapper的Dispose方法,倒序釋放各模塊中加載的資源,在AbpWebApplication的Application_End方法中調用。
public virtual void Dispose() { if (IsDisposed) { return; } IsDisposed = true; //倒序執行全部模塊的Shutdown方法 _moduleManager?.ShutdownModules(); }
AbpModule是一抽象類,全部的模塊都是他的派生類。AbpModule提供PreInitialize,Initialize,PostInitialize,Shutdown四個無參無返回值方法,從名字上就能夠看出AbpModule的生命週期被劃成四部分,其中初始化被分紅了三部分。
ABP的模塊查找基本就是對全部程序集進行遍歷(IAssemblyFinder),再篩選出AbpModule的派生類(ITypeFinder)
private List<Type> FindAllModuleTypes(out List<Type> plugInModuleTypes) { plugInModuleTypes = new List<Type>(); var modules = AbpModule.FindDependedModuleTypesRecursivelyIncludingGivenModule(_modules.StartupModuleType); foreach (var plugInModuleType in _abpPlugInManager.PlugInSources.GetAllModules()) { if (modules.AddIfNotContains(plugInModuleType)) { plugInModuleTypes.Add(plugInModuleType); } } return modules; }
按照依賴關係對它們排序,而後按順序加載。
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()); }
應用關閉時則倒序釋放它們。
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."); }
private void RegisterModules(ICollection<Type> moduleTypes) { foreach (var moduleType in moduleTypes) { _iocManager.RegisterIfNot(moduleType); } } public static bool RegisterIfNot(this IIocRegistrar iocRegistrar, Type type, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton) { if (iocRegistrar.IsRegistered(type)) { return false; } iocRegistrar.Register(type, lifeStyle); return true; }
而IocManager 和Configuration 也是單例,因此因此模塊共享Ioc容器和配置信息。
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>(); var moduleInfo = new AbpModuleInfo(moduleType, moduleObject, plugInModuleTypes.Contains(moduleType)); _modules.Add(moduleInfo); if (moduleType == _modules.StartupModuleType) { StartupModule = moduleInfo; } Logger.DebugFormat("Loaded module: " + moduleType.AssemblyQualifiedName); } }
模塊在初始化時每每須要定義一些初始的變量或參數。ABP經過AbpStartupConfiguration,Castle的依賴注入,Dictionary對象和擴展方法很巧妙的實現了配置中心化。
AbpStartupConfiguration中包含着Setting、Navigation、Location、EventBus、Feature等等核心模塊的配置信息的引用,同時提供了一個IModuleConfigurations 類型的成員用於後期模塊的配置擴展。
在AbpBootstrapper的Initialize方法中能夠看到它的實例化操做。
namespace Mt.Web.Configuration { public interface IAbpWebModuleConfiguration { IAbpAntiForgeryWebConfiguration AntiForgery { get; } IAbpWebLocalizationConfiguration Localization { get; } } public class AbpWebModuleConfiguration : IAbpWebModuleConfiguration { public IAbpAntiForgeryWebConfiguration AntiForgery { get; } public IAbpWebLocalizationConfiguration Localization { get; } public AbpWebModuleConfiguration( IAbpAntiForgeryWebConfiguration antiForgery, IAbpWebLocalizationConfiguration localization) { AntiForgery = antiForgery; Localization = localization; } } }
利用字典的特性,經過一個擴展方法用於添加配置信息,configurations.AbpConfiguration就是IAbpStartupConfiguration。
public static class AbpWebConfigurationExtensions { /// <summary> /// Used to configure ABP Web module. /// </summary> public static IAbpWebModuleConfiguration AbpWeb(this IModuleConfigurations configurations) { return configurations.AbpConfiguration.Get<IAbpWebModuleConfiguration>(); } }
原理:
internal class AbpStartupConfiguration : DictionaryBasedConfig, IAbpStartupConfiguration { /// <summary> /// Reference to the IocManager. /// </summary> public IIocManager IocManager { get; } /// <summary> /// Used to configure modules. /// Modules can write extension methods to <see cref="ModuleConfigurations"/> to add module specific configurations. /// </summary> public IModuleConfigurations Modules { get; private set; } public T Get<T>() { return GetOrCreate(typeof(T).FullName, () => IocManager.Resolve<T>()); } //…… }
public class DictionaryBasedConfig : IDictionaryBasedConfig { /// <summary> /// Dictionary of custom configuration. /// </summary> protected Dictionary<string, object> CustomSettings { get; private set; } /// <summary> /// Gets a configuration object with given name. /// </summary> public T GetOrCreate<T>(string name, Func<T> creator) { var value = Get(name); if (value == null) { value = creator(); Set(name, value); } return (T) value; } //…… }
在AbpModule中有Configurations屬性(IAbpStartupConfiguration,單例),
在AbpModule的PreInitialize(預初始化事件)中會將本模塊的配置信息封裝註冊到IoC容器。
同時預初始化事件中還能夠調整本身或其餘模塊的配置信息,以及經過ReplaceService方法替換內置服務(模塊預初始化方法是按依賴關係順序被執行,因此最後有效的是最後一次替換後的結果)
namespace Mt.Web { [DependsOn(typeof(AbpWebCommonModule))] public class AbpWebModule : AbpModule { /// <inheritdoc/> public override void PreInitialize() { //註冊一些不能依據約定自動註冊的服務。 IocManager.Register<IAbpAntiForgeryWebConfiguration, AbpAntiForgeryWebConfiguration>(); IocManager.Register<IAbpWebLocalizationConfiguration, AbpWebLocalizationConfiguration>(); IocManager.Register<IAbpWebModuleConfiguration, AbpWebModuleConfiguration>(); //替換服務 Configuration.ReplaceService<IPrincipalAccessor, HttpContextPrincipalAccessor>(DependencyLifeStyle.Transient); Configuration.ReplaceService<IClientInfoProvider, WebClientInfoProvider>(DependencyLifeStyle.Transient); //修改內置配置 Configuration.MultiTenancy.Resolvers.Add<DomainTenantResolveContributer>(); Configuration.MultiTenancy.Resolvers.Add<HttpHeaderTenantResolveContributer>(); Configuration.MultiTenancy.Resolvers.Add<HttpCookieTenantResolveContributer>(); AddIgnoredTypes(); //修改擴展配置 Configuration.Modules.AbpWeb().Localization.CookieName = "Abp.Localization.CultureName"; } /// <inheritdoc/> public override void Initialize() { IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); } private void AddIgnoredTypes() { var ignoredTypes = new[] { typeof(HttpPostedFileBase), typeof(IEnumerable<HttpPostedFileBase>), typeof(HttpPostedFileWrapper), typeof(IEnumerable<HttpPostedFileWrapper>) }; foreach (var ignoredType in ignoredTypes) { Configuration.Auditing.IgnoredTypes.AddIfNotContains(ignoredType); Configuration.Validation.IgnoredTypes.AddIfNotContains(ignoredType); } } } }
配置都是以單例的方式註冊的,因此在各模塊中,以及在任何使用它的服務裏,修改和讀取的都是同一組配置數據。
public class MyService : ITransientDependency { private readonly IAbpWebModuleConfiguration _configuration; public MyService(IAbpWebModuleConfiguration configuration) { _configuration = configuration; } public void DoIt() { if (_configuration.Localization.CookieName = "Abp.Localization.CultureName") { //... } } }