[Abp 源码分析]四、模块配置
2018-06-26 18:09
567 查看
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.点此跳转到总目录
相关文章推荐
- Nginx源码分析 - Event事件篇 - Event模块和配置的初始化
- ABP源码分析四十四:ZERO的配置
- ABP源码分析二:ABP中配置的注册和初始化
- ABP源码分析四十六:ABP ZERO中的Ldap模块
- Flume NG源码分析(一)基于静态properties文件的配置模块
- Nginx 源码分析-- 模块module 解析执行 nginx.conf 配置文件流程分析 一
- Nginx 源码分析-- 模块module 解析执行 nginx.conf 配置文件流程分析 二
- Nginx源码分析 - Event事件篇 - Event模块和配置的初始化
- CI源码分析(一)—config配置文件模块
- 5 (phonegap源码分析)通用配置/构建模块(common builder)
- Flume NG源码分析(二)支持运行时动态修改配置的配置模块
- [Abp 源码分析]二、模块系统
- ABP之模块分析
- zrender源码分析3--初始化Painter绘图模块
- Mybatis源码分析-配置模块
- hibernate源码-配置文件加载过程分析
- Yii框架源码分析之文件结构、程序结构及配置
- Servlet容器Tomcat中web.xml中url-pattern的配置详解[附带源码分析]
- 一个普通的 Zepto 源码分析(三) - event 模块
- FastDFS-Nginx扩展模块源码分析