ABP模块化
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ABP模块化相关的知识,希望对你有一定的参考价值。
基于Abp模块化、插件化的设计,开发人员可以将自定义的功能以模块的形式集成到项目中。通常地,一个程序集作为一个模块。如果你的应用是多个程序集,建议为每个程序集定义一个模块。
模块的加载
模块和插件
插件:
模块及插件的加载路线
1. 扩展的HttpApplication对象(在Abp.Web项目中AbpWebApplication<TStartupModule> : HttpApplication)中有AbpBootstrapper成员
AbpWebApplication的Application_Start方法:
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."); }
所有AbpModule的派生类都被创建为单例
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); } }
Configuration的加载
模块在初始化时往往需要定义一些初始的变量或参数。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; } } }
扩展 IAbpStartupConfiguration(提供一个对自定义配置的快捷访问)
利用字典的特性,通过一个扩展方法用于添加配置信息,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") { //... } } }
以上是关于ABP模块化的主要内容,如果未能解决你的问题,请参考以下文章