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 vNext(9):ABP模块化开发-文件管理
Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 目录 前言 开始 创建模块 模块开发 应用服务 运行模块 单元测试 模块使用 最后 前言 在之前的章节中介绍过ABP扩展实体,当时在用户 ...
- abp模块化开发之通用树1:基本使用
一.概述 有些功能在单个项目或多个项目被重复使用,比如:附件,同一个系统中的多个模块都可能使用到,不同项目也有需要.再比如:有无限级分类的树形功能,区域.产品分类.数据字典等.最简单粗暴的办法是直接复 ...
- asp.net abp模块化开发之通用树2:设计思路及源码解析
一.前言 上一篇大概说了下abp通用树形模块如何使用,本篇主要分析下设计思路. 日常开发中会用到很多树状结构的数据,比如:产品的多级分类.省市区县,大多数系统也会用到类似“通用字典/数据字典”的功能, ...
- ABP vnext模块化架构的最佳实践的实现
在上一篇文章<手把手教你用Abp vnext构建API接口服务>中,我们用ABP vnext实现了WebAPI接口服务,但是并非ABP模块化架构的最佳实践.我本身也在学习ABP,我认为AB ...
- 浅谈Abp vNext的模块化设计
abp的模块化给我留下深刻的印象,模块化不是什么新概念,大家都习以为常,但是为什么要模块化,模块化的意义或者说目的是什么?也许我们思考得并不深入.难得的是abp不仅完美的阐述了模块化概念,而且把模块化 ...
- ABP入门系列(15)——创建微信公众号模块
ABP入门系列目录--学习Abp框架之实操演练 源码路径:Github-LearningMpaAbp 1. 引言 现在的互联网已不在仅仅局限于网页应用,IOS.Android.平板.智能家居等平台正如 ...
- [2017-05-31]Abp介绍和经验分享-目录
很久没动博客了,人比较懒. 最近想写点啥,主要就介绍下ABP框架和我这两年的使用经验. 文档翻译之类的工作就算了,需要的请参考: 官方文档 PS:官方文档末尾有中文文档的链接,这里就不贴了 先列个提纲 ...
- ABP文档笔记 - 模块系统 及 配置中心
ABP框架 - 模块系统 ABP框架 - 启动配置 Module System Startup Configuration ABP源码分析三:ABP Module ABP源码分析四:Configura ...
- 初识ABP vNext(1):开篇计划&基础知识
目录 前言 开始 审计(Audit) 本地化(Localization) 事件总线(Event Bus) 多租户(multi-tenancy technology) DDD分层 实体(Entity) ...
随机推荐
- 【转】将grub2安装到u盘的方法
将grub2安装到u盘的方法 时间:2015-03-21来源:linux网站 作者:linux人 grub2在各大linux发行版中广泛采用,它非常强大,基本上大多数操作系统都是通过它引导起来的,它的 ...
- Servlet数据缓存
缓存是提高数据访问能力,降低服务器压力的一种必要的方式,今天我要说的数据缓存方式有两种,1-->session对单个数据访问接口页面的数据进行缓存,2-->单例模式对整个servlet页面 ...
- PHP 装饰器模式
装饰器模式:是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能. [装饰器模式中主要角色] 抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,即可以给这 ...
- freemarker页面中文乱码
一.前言 简单的记录freemarker遇到的错误问题:ftl页面中文乱码 由于freemarker整合在ssm框架中,所以笔者直接贴配置代码 <beans xmlns="http:/ ...
- Java输入输出常用类Scanner
Scaner类,使用获取键盘输入. public boolean DemoTest(){ Scanner input = new Scanner(System.in); System.out.prin ...
- SharePoint文档库文件夹特殊字符转义
当我们在SharePoint网站文档库中新建文件夹时包含了~ " # % & * : < > ? / \ { | }字符时(一共15个), 或者以.开头或者结束,或者包含 ...
- SharePoint2016母版页的直接依赖项的数目限制超过10的限制解决方案Direct Dependencies Limit with Master page User Controls
前言 最近母版制作站点模板,根据站点模板新建后发现如下错误: 解决方案 1. 打开C:\inetpub\wwwroot\wss\VirtualDirectories\,如下图: 2. 打开web.co ...
- dagger2记录篇
作为一个码农,什么都不用多讲,贴代码 build project build module Application public class App extends Application { pri ...
- DAO设计模式
DAO设计模式 DAO设计模式简介: DAO设计模式可以减少代码量,增强程序的可移植性,提高代码的可读性. DAO(数据库操作对象)设计模式是 JavaEE 数据层的操作.主要由五部分组成: 1.数据 ...
- Oracle数据行拆分多行
工作和学习中常常会遇到一行要分割成多行数据的情况,在此整理一下做下对比. 单行拆分 如果表数据只有一行,则可以直接在原表上直接使用connect by+正则的方法,比如: select regexp_ ...