基於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")
{
//...
}
}
}
