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文档笔记 - 模块系统 及 配置中心的更多相关文章
- ABP文档笔记系列
ABP文档笔记 - 模块系统 及 配置中心 ABP文档笔记 - 事件BUS ABP文档笔记 - 数据过滤 ABP文档笔记 - 规约 ABP文档笔记 - 配置.设置.版本.功能.权限 ABP文档笔记 - ...
- ABP文档笔记 - 通知
基础概念 两种通知发送方式 直接发送给目标用户 用户订阅某类通知,发送这类通知时直接分发给它们. 两种通知类型 一般通知:任意的通知类型 "如果一个用户发送一个好友请求,那么通知我" ...
- ABP文档笔记 - 数据过滤
预定义的过滤 ISoftDelete 软删除过滤用来在查询数据库时,自动过滤(从结果中抽取)已删除的实体.如果一个实体可以被软删除,它必须实现ISoftDelete接口,该接口只定义了一个IsDele ...
- ABP文档笔记 - 事件BUS
文档: ABP框架 - 领域事件(EventBus) EventBus & Domain Events ABP源码分析二十五:EventBus EventBus(事件总线) EventBus是 ...
- ABP文档笔记 - 配置、设置、版本、功能、权限
配置 全局仅一个单例,保存一组配置信息,一般直接在模块的预启动事件中赋值or修改.没有Scope划分,无论租户还是房东亦或者用户读取的值都不会有差异.每个模块都可以扩展这个配置. 设置 它没有层级关系 ...
- ABP文档笔记 - 规约
ABP框架 - 规约 简介 规约模式是一个特别的软件设计模式,业务逻辑可以使用boolean逻辑重新链接业务逻辑(维基百科). 实践中的大部分情况,它是为实体或其它业务对象,定义可复用的过滤器. 理解 ...
- ABP文档 - Mvc 控制器
文档目录 本节内容: 简介 AbpController基类 本地化 其它 过滤 异常处理和结果包装 审计日志 验证 授权 工作单元 反伪造 模型绑定器 简介 ABP通过nuget包Abp.Web.Mv ...
- ABP文档 - 目录
ABP框架 概览 介绍 多层结构 模块系统 启动配置 多租户 集成OWIN 共同结构 依赖注入 会话 缓存 日志 设置管理 时间 领域层 实体 值对象(新) 仓储 领域服务 工作单元 领域事件(Eve ...
- Winform开发框架中的内容及文档管理模块功能介绍
在开发项目的时候,我们有一些场景需要编辑一些HTML文档,作为内容发布系统的一部分,有时候也需要对一些文档如WORD文档进行编辑管理,这样需要我们对这些内容及文档进行合适的管理.本文主要介绍在WInf ...
随机推荐
- ntroducing K-Pattern: A Rapid Way to Make CRUD Operations with Entity Framework
Download Source Introduction Earlier in the last year I made a different approach to work on Entity ...
- 开源IMS平台中间件Mobicents
下面内容来自百度百科 Mobicents 是一个高伸缩性.事件驱动的应用服务器.是一款专业的.开放源代码的 VoIP 中间件平台.Mobicents是首个采用JAIN SLEE标准的开放式源代码电信应 ...
- delphi 取json中数组的值(ISuperArray)
{ "action": "******", "data": [ { "Info1": { "ID": ...
- EF 配置实现建表与迁移
通过EF 作为操作数据库的工具有一段时间了,也做了几个相对不大的项目,慢慢的也对EF的使用摸索出来了一些规则,虽然说不是技术难点,但是,我说的是但是,能够提高我们开发效率的棉花糖有时我们还是必须要吃的 ...
- 为什么 kubernetes 天然适合微服务 (2)
此文已由作者刘超授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验 三.微服务化的十个设计要点 微服务有哪些要点呢?第一张图是 SpringCloud 的整个生态. 第二张图是微服 ...
- 【大数据之数据仓库】GreenPlum PK DeepGreen(TPCH)
1.背景 一张UML类图可以简单的说明GreenPlum和DeepGreen之间的关系: GreenPlum: 主页:http://greenplum.org/ 源码:开源,https://githu ...
- C# 密封
到目前位置所说的都是让类如何如何进行继承啊 ,重写啊,巴不得类有十多个继承,超级多的重写. 但是,今天我们来说说不允许继承和不允许重写! 这个不允许继承是包括类和方法. 这种情况好比: 爸爸有私房钱, ...
- 泛型2(lambda表达式/参数绑定)
lambda 表达式: Lambda表达式完整的声明格式如下: [capture list] (params list) mutable exception-> return type { fu ...
- Centos查看端口占用令
Centos查看端口占用情况命令,比如查看80端口占用情况使用如下命令: lsof -i tcp:80 列出所有端口 netstat -ntlp 1.开启端口(以80端口为例) 方法一: /sbin/ ...
- 选择排序 思想 JAVA实现
已知一个数组 9.29.95.47.79.37.18.56.96.22 使用选择排序是数组有序 选择排序同样是一个运行时间为O(N²)的排序算法. 算法思想:(以从小到大为例) 9.29.95.47. ...