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) ...
随机推荐
- 【重大更新】开源跨平台物联网通讯框架ServerSuperIO 2.0(SSIO)下载
更新具体细节参见:[更新设计]跨平台物联网通讯框架ServerSuperIO 2.0 ,功能.BUG.细节说明,以及升级思考过程! 声明:公司在建设工业大数据平台,SSIO正好能派上用场,所以抓紧时间 ...
- Eclipse 日期和时间格式自定义
点击下载Eclipse插件 org.eclipse.text_3.5.300.v20130515-1451.jar 覆盖下图所示的jar文件. /************************* ...
- 使用CSS3制作导航条和毛玻璃效果
导航条对于每一个Web前端攻城狮来说并不陌生,但是毛玻璃可能会相对陌生一些.简单的说,毛玻璃其实就是让图片或者背景使用相应的方法进行模糊处理.这种效果对用户来说是十分具有视觉冲击力的. 本次分享的主题 ...
- gulp-uglify 与gulp.watch()配合使用时遇到的重复压缩问题
今天学习gulp时候,用到gulp-uglify压缩js模块,遇到的一个问题-当用gulp.watch来监听js文件的变动时出现重复压缩的问题 目录结构如下: gulpfile.js代码如下: var ...
- 你必须知道的HTTP错误
发送网络请求有时失败,分析一下响应行,在响应的响应行内,你会发现响应行由三部分组成,用空格来隔开,HTTP/1.1 404 NOT FOUND,第一个是响应的HTTP的版本,第二个和第三个是状态值. ...
- Ionic设置ion-slide-box不启用(不通过$ionicSlideBoxDelegate)
猛地一看这个标题,可能觉得多此一举,直接$ionicSlideBoxDelegate. $getByHandle(handle). enableSlide(false)设置不就行了?是的,按理说就是这 ...
- iOS App 不支持http协议 App Transport Security has blocked a cleartext HTTP (http://)
目前iOS已经不支持http协议了,不过可以通过info.plist设置允许 App Transport Security has blocked a cleartext HTTP (http://) ...
- SQL 数据优化之不建立索引的情况
索引可以提高数据的检索效率,也可以降低数据库的IO成本,并且索引还可以降低数据库的排序成本.排序分组操作主要消耗的就是CPU资源和内存,所以能够在排序分组操作中好好的利用索引将会极大地降低CPU资源的 ...
- Oracle SGA优化
oracle的SGA:数据库的系统全局区,SGA主要由三部分构成:共享池.数据缓冲区.日志缓冲区. 共享池又由两部分构成:共享SQL区和数据字典缓冲区. 共享SQL区专门存放用户SQL命令,oracl ...
- 关于Redis的ACID
事务是一个数据库必备的元素,对于redis也不例外,对于一个传统的关系型数据库来说,数据库事务满足ACID四个特性: A代表原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全 ...