[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的信 ...
随机推荐
- Webpack3 从入门到放弃
我是网络的搬运工,具体请看:Webpack 3,从入门到放弃
- python全栈开发day113-DBUtils(pymysql数据连接池)、Request管理上下文分析
1.DBUtils(pymysql数据连接池) import pymysql from DBUtils.PooledDB import PooledDB POOL = PooledDB( creato ...
- RESTful-3架构详解
1. 什么是REST REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移. 它首次出现在2000年Roy Fielding的 ...
- input autocomplete属性设计输入框自动联想(php实现)
因为客户临时要求加一个输入框自动联想,就开始了解这块.结合网上总结最难的一点就是找好对应的js版本以及相应的jQuery-ui: 以下是我用的版本,以及连接地址: jQuery的js文件: <s ...
- js中将字符串作为函数名来调用的方法
方法:eval() eg: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> ...
- nginx.conf 中php-ftp配置
location ~ .php$ { root /home/www; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_par ...
- 读 Spring实战 遇到的问题记录(一)
package soundsystem; import beanConfig.CDPlayerConfig; import org.junit.Rule; import org.junit.Test; ...
- Pytorch多GPU训练
Pytorch多GPU训练 临近放假, 服务器上的GPU好多空闲, 博主顺便研究了一下如何用多卡同时训练 原理 多卡训练的基本过程 首先把模型加载到一个主设备 把模型只读复制到多个设备 把大的batc ...
- jquery项目中一些常用方法
1.获取url中的参数 function getUrlParam(name) { var reg = new RegExp("(^|&)" + name + &quo ...
- 3.1circle
就是括号匹配的题目,如果有交集就是NO #include<iostream> #include<cstring> #include<stdio.h> #includ ...