ABP模块化

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ABP模块化相关的知识,希望对你有一定的参考价值。

ABP框架 - 模块系统

ABP框架 - 启动配置

Module System

Startup Configuration

ABP源码分析三:ABP Module

ABP源码分析四:Configuration

 

基于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系统设置

ABP中的模块初始化过程

ABP中模块初始化过程

ABP模块化

ABP模块化

ABP模块配置