ABP文檔筆記 - 模塊系統 及 配置中心 ABP框架 - 模塊系統 ABP框架 - 啓動配置

ABP框架 - 模塊系統html

ABP框架 - 啓動配置web

Module Systemapp

Startup Configuration框架

ABP源碼分析三:ABP Moduleide

ABP源碼分析四:Configuration模塊化

 

基於Abp模塊化、插件化的設計,開發人員能夠將自定義的功能以模塊的形式集成到項目中。一般地,一個程序集做爲一個模塊。若是你的應用是多個程序集,建議爲每一個程序集定義一個模塊。源碼分析

模塊的加載

模塊和插件

插件:post

模塊及插件的加載路線

 1. 擴展的HttpApplication對象(在Abp.Web項目中AbpWebApplication<TStartupModule> : HttpApplication)中有AbpBootstrapper成員this

AbpWebApplication的Application_Start方法:url

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")
        {
            //...
        }
    }
}
相關文章
相關標籤/搜索