[Abp 源码分析]四、模块配置
0.简要介绍
在 Abp 框架当中通过各种 Configuration 来实现模块的配置,Abp 本身提供的很多基础设施功能的一些在运行时的行为是通过很多不同的 Configuration 来开放给用户进行一些自定义配置的。
比如说缓存模块,我要配置缓存的过期时间,Abp 默认是 1 个小时,但是我也可以自己来定义,直接赋值或者从配置项来读取都是由具体使用者来控制的,所以 Abp 通过各种 Configuration 类来控制一些运行时参数。
这些 Abp 本身基础设施的配置类都是存放在 \Abp\src\Abp\Configuration\Startup\ 这个文件夹内部的,我们来看一下他们的依赖关系。

1.启动流程
从上图可以看到在 IAbpStartupConfiguration 内部拥有诸多引用(可能没有列举完成,可以在其定义看到),基本上 Abp 自己的基础设施配置都在这里面。
那么 IAbpStartupConfiguration 自己内部的这些属性是在哪儿初始化的呢,其实就是在之前讲过的 AbpBootstrapper 的 Initialize() 内部初始化的。再看下代码:
public virtual void Initialize()
{
try
{
// 其他代码
IocManager.IocContainer.Install(new AbpCoreInstaller());
IocManager.Resolve<AbpStartupConfiguration>().Initialize();
// 其他代码
}
catch (Exception ex)
{
_logger.Fatal(ex.ToString(), ex);
throw;
}
}
在 AbpCoreInstaller 类内部之前也说过,在这里面统一注入了这些 Configuration 的单例,同时解析出 AbpStartupConfiguration ,调用其 Initialzie() 方法来对自己的那些 xxxConfiguration 接口赋值,代码如下:
public void Initialize()
{
Localization = IocManager.Resolve<ILocalizationConfiguration>();
Modules = IocManager.Resolve<IModuleConfigurations>();
Features = IocManager.Resolve<IFeatureConfiguration>();
Navigation = IocManager.Resolve<INavigationConfiguration>();
Authorization = IocManager.Resolve<IAuthorizationConfiguration>();
Validation = IocManager.Resolve<IValidationConfiguration>();
Settings = IocManager.Resolve<ISettingsConfiguration>();
UnitOfWork = IocManager.Resolve<IUnitOfWorkDefaultOptions>();
EventBus = IocManager.Resolve<IEventBusConfiguration>();
MultiTenancy = IocManager.Resolve<IMultiTenancyConfig>();
Auditing = IocManager.Resolve<IAuditingConfiguration>();
Caching = IocManager.Resolve<ICachingConfiguration>();
BackgroundJobs = IocManager.Resolve<IBackgroundJobConfiguration>();
Notifications = IocManager.Resolve<INotificationConfiguration>();
EmbeddedResources = IocManager.Resolve<IEmbeddedResourcesConfiguration>();
EntityHistory = IocManager.Resolve<IEntityHistoryConfiguration>();
CustomConfigProviders = new List<ICustomConfigProvider>();
ServiceReplaceActions = new Dictionary<Type, Action>();
}
所以,在模块定义的基类 AbpModule 当中,早就注入了 IAbpStartupConfiguration 接口,让你很方便的就可以在模块的预加载的时候配置各种基础设施的参数。举个栗子:
public override void PreInitialize()
{
Configuration.Caching.ConfigureAll(z=>z.DefaultSlidingExpireTime = TimeSpan.FromHours(1));
}

可以看到这里我们的 Configuration 属性其实就是 IAbpStartupConfiguration 接口。
2.代码分析
2.1自定义模块配置
我们可以看到 IAbpStartupConfiguration 除了自己拥有大量基础设施的配置类,同时他还继承一个基类叫做 DictionaryBasedConfig ,那么 Abp 框架为什么要这么写呢?
其实这个基类的作用就是存放用户自定义的 Configuration 类型的,细心观察的话会发现在 AbpStartupConfiguration 的内部有一个 Get 方法,该方法就是用来获取存储的配置类型。
public T Get<T>()
{
// 调用基类的 GetOrCreate 方法,不存在的话直接从 IocContainer 中解析
return GetOrCreate(typeof(T).FullName, () => IocManager.Resolve<T>());
}
在 DictionaryBasedConfig 中维护了一个字典 CustomSettings ,其 Key/Value 类型为 string/object ,因为在 Abp 框架当中是不知道你自定义模块配置类的类型的,所以存了一个 object 对象。
然后就有以下用法,首先在模块 PreInitialize() 方法当中注入你需要注入的配置类:
public override void PreInitialize()
{
// 注入配置类
IocManager.Register<IAbpAspNetCoreConfiguration, AbpAspNetCoreConfiguration>();
// 替换服务,后面讲解
Configuration.ReplaceService<IPrincipalAccessor, AspNetCorePrincipalAccessor>(DependencyLifeStyle.Transient);
Configuration.ReplaceService<IAbpAntiForgeryManager, AbpAspNetCoreAntiForgeryManager>(DependencyLifeStyle.Transient);
Configuration.ReplaceService<IClientInfoProvider, HttpContextClientInfoProvider>(DependencyLifeStyle.Transient);
}
然后针对 IModuleConfigurations 写一个扩展方法,因为在 IModuleConfigurations 内部就有一个 IAbpAspNetCoreConfiguration 的实例,IModuleConfigurations 的注释就说该接口是用于配置模块的,模块可以通过编写扩展方法来添加自己的 Configuration 类:
public static class AbpAspNetCoreConfigurationExtensions
{
/// <summary>
/// Used to configure ABP ASP.NET Core module.
/// </summary>
public static IAbpAspNetCoreConfiguration AbpAspNetCore(this IModuleConfigurations configurations)
{
// 两种写法都差不多
return configurations.AbpConfiguration.GetOrCreate("AbpModule", () => IocManager.Resolve<IAbpAspNetCoreConfiguration>());
return configurations.AbpConfiguration.Get<IAbpAspNetCoreConfiguration>();
}
}
2.2 服务实现替换
在 Abp 当中允许我们替换一些他本身的一些实现,只要你是在模块进行预加载的时候替换的话,都是可以的。而 Abp 他本身在 IAbpStartupConfiguration 当中提供了一个方法叫做 ReplaceService() 方法专门来让你替换服务。
我们来看一下他的定义:
void ReplaceService(Type type, Action replaceAction);
emmmm,传入一个 Type 和 Action,咋跟我看到的不一样呢,Ctrl + N 搜索了一下,发现在模块里面使用的 ReplaceService() 方法是存放在 AbpStartupConfigurationExtensions 里面编写的一个静态方法,其定义如下:
public static void ReplaceService<TType, TImpl>(this IAbpStartupConfiguration configuration, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton)
where TType : class
where TImpl : class, TType
{
configuration.ReplaceService(typeof(TType), () =>
{
configuration.IocManager.Register<TType, TImpl>(lifeStyle);
});
}
我来看看,传入一个 Type 和 一个 Action ,Type 用来调用 IAbpStartupConfiguration 的同名方法,Action 则是用来注册组件的。
原来如此,我们再来到 IAbpStartupConfiguration.ReplaceService(Type type, Action replaceAction) 的具体实现:
public Dictionary<Type, Action> ServiceReplaceActions { get; private set; }
public void ReplaceService(Type type, Action replaceAction)
{
ServiceReplaceActions[type] = replaceAction;
}
唔,就是一个字典嘛,我们来看看在什么地方用到过它。
public override void Initialize()
{
foreach (var replaceAction in ((AbpStartupConfiguration)Configuration).ServiceReplaceActions.Values)
{
replaceAction();
}
// 其他代码
}
最后我们看到在 AbpKernelModule 的 Initialize() 方法里面就会遍历这个字典,来调用之前存入的 Action 。
因为 Abp 所有组件的注册都是在模块 Initialize() 内部来进行注册的,而这串代码刚好又放在 AbpKernelModule 的初始化方法的第一行就开始执行,所以确保你替换的组件能够在 Abp 内部组件注册前执行。
所以当你要替换 Abp 内置组件服务的时候一定要记住在模块的 PreInitialize() 里面执行哦~
3. 扩展:Abp 支持多数据库
如果你的 Abp 项目有多个数据库上下文实体的时候怎么办呢?
在 Abp 官方 Demo 当中就有说明,你可以通过替换默认的 IConnectionStringResolver 来实现不同数据库的解析哦~,我们继承 DefaultConnectionStringResolver 实现一个 MulitDbContextConnectionStringResolver。
public class MulitDbContextConnectionStringResolver : DefaultConnectionStringResolver
{
public HKERPConnectionStringResolver(IAbpStartupConfiguration configuration)
: base(configuration)
{
}
public override string GetNameOrConnectionString(ConnectionStringResolveArgs args)
{
if (args["DbContextConcreteType"] as Type == typeof(ADbContext))
{
var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());
// 返回 ADbContext 的 ConnectionString
return configuration.GetConnectionString(AllConsts.ADbConnectionStringName);
}
if (args["DbContextConcreteType"] as Type == typeof(BDbContext))
{
var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());
// 返回 BDbContext 的 ConnectionString
return configuration.GetConnectionString(HKERPCRMConsts.BDbConnectionStringName);
}
// 都不是则使用默认的数据库连接字符串
return base.GetNameOrConnectionString(args);
}
}
然后在我们的 EFCore 模块的预加载方法当中加入以下代码:
Configuration.ReplaceService(typeof(IConnectionStringResolver), () =>
{
IocManager.IocContainer.Register(
Component.For<IConnectionStringResolver, IDbPerTenantConnectionStringResolver>()
.ImplementedBy<MulitDbContextConnectionStringResolver>()
.LifestyleTransient()
);
});
当然你也不要忘记在后面通过 AddDbContext() 方法来把你的数据库上下文添加到 Abp 里面去哦。
Configuration.Modules.AbpEfCore().AddDbContext<ADbContext>(options=>{ /*配置代码*/});
Configuration.Modules.AbpEfCore().AddDbContext<BDbContext>(options=>{ /*配置代码*/});
3.点此跳转到总目录
[Abp 源码分析]四、模块配置的更多相关文章
- 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入
使用react全家桶制作博客后台管理系统 前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...
- ABP源码分析四十四:ZERO的配置
ABP Zero模块中需要配置的地方主要集中在三块:配置静态的role,配置外部认证源,以及配置本地化语言和资源. UserManagementConfig/IUserManagementConfig ...
- ABP源码分析四十五:ABP ZERO中的EntityFramework模块
AbpZeroDbContext:配置ABP.Zero中定义的entity的Dbset EntityFrameworkModelBuilderExtensions:给PrimitiveProperty ...
- ABP源码分析四十六:ABP ZERO中的Ldap模块
通过AD作为用户认证的数据源.整个管理用户认证逻辑就在LdapAuthenticationSource类中实现. LdapSettingProvider:定义LDAP的setting和提供Defaut ...
- ABP源码分析四:Configuration
核心模块的配置 Configuration是ABP中设计比较巧妙的地方.其通过AbpStartupConfiguration,Castle的依赖注入,Dictionary对象和扩展方法很巧妙的实现了配 ...
- ABP源码分析四十三:ZERO的本地化
ABP Zero模块扩展了ABP基础框架中的本地化功能,实现了通过数据库管理本地化的功能.其通过数据库保存本地化语言及其资源. ApplicationLanguage:代表本地化语言的实体类.一种语言 ...
- ABP源码分析四十:ZERO的Application和Tenant
ABP的Zero模块以数据库为数据源实现了ABP框架中的tenant management (multi-tenancy), role management, user management, ses ...
- ABP源码分析四十二:ZERO的身份认证
ABP Zero模块通过自定义实现Asp.Net Identity完成身份认证功能, 对Asp.Net Identity做了较大幅度的扩展.同时重写了ABP核心模块中的permission功能,以实现 ...
- ABP源码分析四十七:ABP中的异常处理
ABP 中异常处理的思路是很清晰的.一共五种类型的异常类. AbpInitializationException用于封装ABP初始化过程中出现的异常,只要抛出AbpInitializationExce ...
- ABP源码分析四十一:ZERO的Audit,Setting,Background Job
AuditLog: 继承自Entity<long>的实体类.封装AuditLog的信息. AuditingStore: 实现了IAuditingStore接口,实现了将AuditLog的信 ...
随机推荐
- IntelliJ IDE 基础经验备案
1.配置本地的JAVA环境 2.配置本地安装的Maven环境 详情 1.配置本地的JAVA环境 准备: 本地已经安装java环境,目录:C:\Program Files\Java\jdk1.8.0_1 ...
- 业务线接入前端异常监控sentry
1.前端异常处理的框架对比 是否开源 收费 语言 监控范围 sentry 是 自己搭建服务器(免费)价格 英文 Angular.AngularJs.Backbone.Ember.JavaScrip ...
- 运行报错:java.io.IOException: invalid constant type: 15
jdk,tomcat更新到jdk1.8与 tomcat8 运行报错:java.io.IOException: invalid constant type: 15 pom.xml文件中更新javassi ...
- 图论之最短路径floyd算法
Floyd算法是图论中经典的多源最短路径算法,即求任意两点之间的最短路径. 它可采用动态规划思想,因为它满足最优子结构性质,即最短路径序列的子序列也是最短路径. 举例说明最优子结构性质,上图中1号到5 ...
- C# 显示纯文本对齐封装(控制显示字体长度)
坑: 用户在写多行的纯文本上来了一个对齐的表格..如下: 原因:不同的字体下,中文,英文大写,英文小写,字符,尤其是空格..字体占用的长度是不一样的,然后显示出来就是乱的.. 然而客户要求在不同的字体 ...
- 07flask中session及cookie的用法。
一,基本概念. 1,session的概念. session和cookie的作用有点类似,都是为了存储用户相关的信息.不同的是,cookie是存储在本地浏览器,而session是存储在服务器.存储在服务 ...
- [CF364D]Ghd
[CF364D]Ghd 题目大意: 有\(n(n\le10^6)\)个数\(A_{1\sim n}(A_i\le10^{12})\),从中选取\(\lceil\frac n2\rceil\)个数,使得 ...
- Python3 文件
f=open('C:\\Users\\fengx\\Desktop\\sharing\\test.txt') 如果打开文件的格式不匹配,可能会报如下错: >>> open('C:\U ...
- Ubuntu安装MySQL和Python库MySQLdb步骤
一.安装MySQL服务器和客户端 执行以下命令: sudo apt-get install mysql-server-5.6 mysql-client-5.6 sudo apt-get install ...
- vue-cli利用router创建单页面
一.创建脚手架 第一步 打开命令输入vue init webpack-simple vue-name 下载webpack-simple (vue-name是你要创建的项目名称) 第二步 输入cd v ...