ABP文档笔记 - 模块系统 及 配置中心

时间:2022-09-14 10:14:27

ABP框架 - 模块系统

ABP框架 - 启动配置

Module System

Startup Configuration

ABP源码分析三:ABP Module

ABP源码分析四:Configuration

基于Abp模块化、插件化的设计,开发人员可以将自定义的功能以模块的形式集成到项目中。通常地,一个程序集作为一个模块。如果你的应用是多个程序集,建议为每个程序集定义一个模块。

模块的加载

模块和插件

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对象和扩展方法很巧妙的实现了配置中心化。

ABP文档笔记 - 模块系统 及 配置中心

AbpStartupConfiguration中包含着Setting、Navigation、Location、EventBus、Feature等等核心模块的配置信息的引用,同时提供了一个IModuleConfigurations 类型的成员用于后期模块的配置扩展。

ABP文档笔记 - 模块系统 及 配置中心

  在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文档笔记 - 模块系统 及 配置中心的更多相关文章

  1. ABP文档笔记系列

    ABP文档笔记 - 模块系统 及 配置中心 ABP文档笔记 - 事件BUS ABP文档笔记 - 数据过滤 ABP文档笔记 - 规约 ABP文档笔记 - 配置.设置.版本.功能.权限 ABP文档笔记 - ...

  2. ABP文档笔记 - 通知

    基础概念 两种通知发送方式 直接发送给目标用户 用户订阅某类通知,发送这类通知时直接分发给它们. 两种通知类型 一般通知:任意的通知类型 "如果一个用户发送一个好友请求,那么通知我&quot ...

  3. ABP文档笔记 - 数据过滤

    预定义的过滤 ISoftDelete 软删除过滤用来在查询数据库时,自动过滤(从结果中抽取)已删除的实体.如果一个实体可以被软删除,它必须实现ISoftDelete接口,该接口只定义了一个IsDele ...

  4. ABP文档笔记 - 事件BUS

    文档: ABP框架 - 领域事件(EventBus) EventBus & Domain Events ABP源码分析二十五:EventBus EventBus(事件总线) EventBus是 ...

  5. ABP文档笔记 - 配置、设置、版本、功能、权限

    配置 全局仅一个单例,保存一组配置信息,一般直接在模块的预启动事件中赋值or修改.没有Scope划分,无论租户还是房东亦或者用户读取的值都不会有差异.每个模块都可以扩展这个配置. 设置 它没有层级关系 ...

  6. ABP文档笔记 - 规约

    ABP框架 - 规约 简介 规约模式是一个特别的软件设计模式,业务逻辑可以使用boolean逻辑重新链接业务逻辑(*). 实践中的大部分情况,它是为实体或其它业务对象,定义可复用的过滤器. 理解 ...

  7. ABP文档 - Mvc 控制器

    文档目录 本节内容: 简介 AbpController基类 本地化 其它 过滤 异常处理和结果包装 审计日志 验证 授权 工作单元 反伪造 模型绑定器 简介 ABP通过nuget包Abp.Web.Mv ...

  8. ABP文档 - 目录

    ABP框架 概览 介绍 多层结构 模块系统 启动配置 多租户 集成OWIN 共同结构 依赖注入 会话 缓存 日志 设置管理 时间 领域层 实体 值对象(新) 仓储 领域服务 工作单元 领域事件(Eve ...

  9. Winform开发框架中的内容及文档管理模块功能介绍

    在开发项目的时候,我们有一些场景需要编辑一些HTML文档,作为内容发布系统的一部分,有时候也需要对一些文档如WORD文档进行编辑管理,这样需要我们对这些内容及文档进行合适的管理.本文主要介绍在WInf ...

随机推荐

  1. 【转】vim格式化C代码

    转自:http://blog.chinaunix.net/uid-24774106-id-3396220.html 在自己的目录下编辑自己的.vimrc, vim ~/.vimrc 添加下面的几行: ...

  2. oracle 直接客户端使用

    到oracle网站下载直接客户端,http://www.oracle.com/technology/software/tech/oci/instantclient/htdocs/winsoft.htm ...

  3. WebSocket小插件

    一.WebSocket小介绍 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信 ...

  4. uboot引导linux内核过程详解【转】

    http://blog.chinaunix.net/uid-7828352-id-4472376.html 写的不错,尤其是uboot向linux内核传递参数的过程写的比较详细.

  5. 【English EMail】2019 Q2 Public Holiday Announcement

    Hi all, According to 2019 public holiday announcement released by *, this is to ann ...

  6. 【Python】函数总结

    以下为自学笔记内容,仅供参考. 转发请保留原文链接https://www.cnblogs.com/it-dennis/p/10516688.html python中的函数 最近看了python中关于函 ...

  7. Akka-CQRS(5)- CQRS Writer Actor 部署和测试

    上篇我们做了一个WriterActor的例子,主要目的是示范WriterActor如何作为集群分片用persistentActor特性及event-sourcing模式实现CQRS的写功能.既然是集群 ...

  8. Debug 路漫漫-05

    Debug 路漫漫-05: 1.使用这种方式计算 AUC 指标,结果出来居然是 NAN, —— 分母为(M*N),M或者N必有一个为0 了.(nan出现的情况绝大部分是分母出现0了)   若分子为0的 ...

  9. &lpar;笔记&rpar;Mysql命令mysql:连接Mysql数据库

    mysql命令用户连接数据库. mysql命令格式: mysql -h主机地址 -u用户名 -p用户密码 1) 连接到本机上的MYSQL首先打开DOS窗口,然后进入目录mysql\bin,再键入命令m ...

  10. &lbrack;EffectiveC&plus;&plus;&rsqb;item33:避免遮掩继承而来的名称。

    先看看: ZT C++ 重载.覆盖和隐藏的区别 http://www.cnblogs.com/jeanschen/p/3405987.html 隐藏是指派生类的函数屏蔽了与其同名的基类函数,规则如下: ...