日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

[Abp vNext 源码分析] - 2. 模块系统的变化

發(fā)布時間:2023/12/4 windows 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [Abp vNext 源码分析] - 2. 模块系统的变化 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、簡要說明

本篇文章主要分析 Abp vNext 當中的模塊系統(tǒng),從類型構(gòu)造層面上來看,Abp vNext 當中不再只是單純的通過?AbpModuleManager?來管理其他的模塊,它現(xiàn)在則是?IModuleManager?和?IModuleLoader 來協(xié)同工作,其他的代碼邏輯并無太大變化。

Abp vNext 規(guī)定每個模塊必須繼承自?IAbpModule 接口,這樣 vNext 系統(tǒng)在啟動的時候才會掃描到相應的模塊。與原來 Abp 框架一樣,每個模塊可以通過 DependsOnAttribute 特性來確定依賴關(guān)系,算法還是使用拓撲排序算法,來根據(jù)依賴性確定模塊的加載順序。(從最頂層的模塊,依次加載,直到啟動模塊。)

以我們的 Demo 項目為例,這里通過拓撲排序之后的依賴關(guān)系如上圖,這樣最開始執(zhí)行的即?AbpDataModule?模塊,然后再是?AbpAuditingModule?以此類推,直到我們的啟動模塊?DemoAppModule。

在 Abp vNext 當中,所有的組件庫/第三方庫都是以模塊的形式呈現(xiàn)的,模塊負責管理整個庫的生命周期,包括注冊組件,配置組件,銷毀組件等。

在最開始的 Abp 框架當中,一個模塊有 4 個生命周期,它們都是在抽象基類?AbpModule?當中定義的,分別是?預加載初始化初始化完成銷毀。前三個生命周期是依次執(zhí)行的 預加載->初始化->初始化完成,而最后一個銷毀動作則是在程序終止的時候,通過 AbpModuleManager?遍歷模塊,調(diào)用其?ShutDown() 方法進行銷毀動作。

新的 Abp vNext 框架除了原有的四個生命周期以外,還抽象出了?IOnPreApplicationInitialization、IOnApplicationInitialization、IOnPostApplicationInitialization、IOnApplicationShutdown。從名字就可以看出來,新的四個生命周期是基于應用程序級別的,而不是模塊級別。

這是什么意思呢?在 Abp vNext 框架當中,模塊按照功能用途劃分為兩種類型的模塊。第一種是 框架模塊,它是框架的核心模塊,比如緩存、EF Core 等基礎設施就屬于框架模塊,其模塊的邏輯與處理基本都在傳統(tǒng)的三個生命周期進行處理。

在我們的?services.AddApplication()?階段就已經(jīng)完成所有初始化,可以給?應用程序模塊 提供服務。

第二種則是?應用程序模塊,這種模塊則是實現(xiàn)了特定的業(yè)務/功能,例如身份管理、租戶管理等,而新增加的四個生命周期基本是為這種類型的模塊服務的。

在代碼和結(jié)構(gòu)上來說,兩者并沒有區(qū)別,在這里僅僅是按用途進行了一次分類。單就模塊系統(tǒng)來說,其基本的作用就類似于一個配置類,配置某種組件的各種參數(shù)和一些默認邏輯。

二、源碼分析

2.1 模塊系統(tǒng)的基礎設施

模塊的初始化動作是在?AbpApplicationBase 基類開始的,在該基類當中除了注入模塊相關(guān)的基礎設施以外。還定義了模塊的初始化方法,即 LoadModules()?方法,在該方法內(nèi)部是調(diào)用的?IModuleLoader 去執(zhí)行具體的加載操作。

internal AbpApplicationBase(
[NotNull] Type startupModuleType,
[NotNull] IServiceCollection services,
[CanBeNull] Action<AbpApplicationCreationOptions> optionsAction
)
{
Check.NotNull(startupModuleType, nameof(startupModuleType));
Check.NotNull(services, nameof(services));


StartupModuleType = startupModuleType;
Services = services;

services.TryAddObjectAccessor<IServiceProvider>();

var options = new AbpApplicationCreationOptions(services);
optionsAction?.Invoke(options);


services.AddSingleton<IAbpApplication>(this);
services.AddSingleton<IModuleContainer>(this);

services.AddCoreServices();

services.AddCoreAbpServices(this, options);


Modules = LoadModules(services, options);
}

private IReadOnlyList<IAbpModuleDescriptor> LoadModules(IServiceCollection services, AbpApplicationCreationOptions options)
{

return services
.GetSingletonInstance<IModuleLoader>()
.LoadModules(
services,
StartupModuleType,
options.PlugInSources
);
}

2.2 模塊的初始化

進入?IModuleLoader?的默認實現(xiàn)?ModuleLoader,在它的?LoadModules() 方法中,基本邏輯如下:

  • 掃描當前應用程序的所有模塊類,并構(gòu)建模塊描述對象。

  • 基于模塊描述對象,使用拓撲排序算法來按照模塊的依賴性進行排序。

  • 排序完成之后,遍歷排序完成的模塊描述對象,依次執(zhí)行它們的三個生命周期方法。

  • public IAbpModuleDescriptor[] LoadModules(
    IServiceCollection services,
    Type startupModuleType,
    PlugInSourceList plugInSources
    )
    {

    Check.NotNull(services, nameof(services));
    Check.NotNull(startupModuleType, nameof(startupModuleType));
    Check.NotNull(plugInSources, nameof(plugInSources));


    var modules = GetDescriptors(services, startupModuleType, plugInSources);


    modules = SortByDependency(modules, startupModuleType);


    ConfigureServices(modules, services);

    return modules.ToArray();
    }

    在搜索模塊類型的時候,是使用的?AbpModuleHelper?工具類提供的?.FindAllModuleTypes() 方法。該方法會將我們的啟動模塊傳入,根據(jù)模塊上面的 DependsOn()?標簽遞歸構(gòu)建?模塊描述對象 的集合。

    private List<IAbpModuleDescriptor> GetDescriptors(
    IServiceCollection services,
    Type startupModuleType,
    PlugInSourceList plugInSources
    )
    {

    var modules = new List<AbpModuleDescriptor>();


    FillModules(modules, services, startupModuleType, plugInSources);

    SetDependencies(modules);


    return modules.Cast<IAbpModuleDescriptor>().ToList();
    }

    protected virtual void FillModules(
    List<AbpModuleDescriptor> modules,
    IServiceCollection services,
    Type startupModuleType,
    PlugInSourceList plugInSources
    )
    {

    foreach (var moduleType in AbpModuleHelper.FindAllModuleTypes(startupModuleType))
    {
    modules.Add(CreateModuleDescriptor(services, moduleType));
    }


    }

    走進?AbpModuleHelper?靜態(tài)類,其代碼與結(jié)構(gòu)與原有的 Abp 框架類似,首先看下它的?FindAllModuleTypes() 方法,根據(jù)啟動模塊的類型遞歸查找所有的模塊類型,并添加到一個集合當中。

    public static List<Type> FindAllModuleTypes(Type startupModuleType)
    {
    var moduleTypes = new List<Type>();

    AddModuleAndDependenciesResursively(moduleTypes, startupModuleType);
    return moduleTypes;
    }

    private static void AddModuleAndDependenciesResursively(List<Type> moduleTypes, Type moduleType)
    {

    AbpModule.CheckAbpModuleType(moduleType);


    if (moduleTypes.Contains(moduleType))
    {
    return;
    }

    moduleTypes.Add(moduleType);


    foreach (var dependedModuleType in FindDependedModuleTypes(moduleType))
    {
    AddModuleAndDependenciesResursively(moduleTypes, dependedModuleType);
    }
    }

    public static List<Type> FindDependedModuleTypes(Type moduleType)
    {
    AbpModule.CheckAbpModuleType(moduleType);

    var dependencies = new List<Type>();


    var dependencyDescriptors = moduleType
    .GetCustomAttributes()
    .OfType<IDependedTypesProvider>();


    foreach (var descriptor in dependencyDescriptors)
    {

    foreach (var dependedModuleType in descriptor.GetDependedTypes())
    {
    dependencies.AddIfNotContains(dependedModuleType);
    }
    }

    return dependencies;
    }

    以上操作完成之后,我們就能獲得一個平級的模塊描述對象集合,我們?nèi)绻褂猛負渑判騺碇匦箩槍@個集合進行排序,就需要知道每個模塊的依賴項,根據(jù)?IAbpModuleDescriptor?的定義,我們可以看到它有一個?Dependencies 集合來存儲它的依賴項。

    public interface IAbpModuleDescriptor
    {

    Type Type { get; }


    Assembly Assembly { get; }


    IAbpModule Instance { get; }


    bool IsLoadedAsPlugIn { get; }


    IReadOnlyList<IAbpModuleDescriptor> Dependencies { get; }
    }

    而?SetDependencies(List<AbpModuleDescriptor> modules) 方法就是來設置每個模塊的依賴項的,代碼邏輯很簡單。遍歷之前的平級模塊描述對象集合,根據(jù)當前模塊的類型定義,找到其依賴項的類型定義。根據(jù)這個類型定義去平級的模塊描述對象集合搜索,將搜索到的結(jié)果存儲到當前的模塊描述對象中的 Dependencies 屬性當中。

    protected virtual void SetDependencies(List<AbpModuleDescriptor> modules)
    {

    foreach (var module in modules)
    {
    SetDependencies(modules, module);
    }
    }

    protected virtual void SetDependencies(List<AbpModuleDescriptor> modules, AbpModuleDescriptor module)
    {

    foreach (var dependedModuleType in AbpModuleHelper.FindDependedModuleTypes(module.Type))
    {

    var dependedModule = modules.FirstOrDefault(m => m.Type == dependedModuleType);
    if (dependedModule == null)
    {
    throw new AbpException("Could not find a depended module " + dependedModuleType.AssemblyQualifiedName + " for " + module.Type.AssemblyQualifiedName);
    }


    module.AddDependency(dependedModule);
    }
    }

    最后的拓撲排序就不在贅述,關(guān)于拓撲排序的算法,可以在我的?這篇 博文當中找到。

    關(guān)于模塊的最后操作,就是執(zhí)行模塊的三個生命周期方法了,這塊代碼在?ConfigureServices() 方法當中,沒什么特別的的處理,遍歷整個模塊描述對象集合,依次執(zhí)行幾個方法就完了。

    只是在這里的生命周期方法與之前的不一樣了,這里會為每個方法傳入一個服務上下文對象,主要是可以通過?IServiceCollection?來配置各個模塊的參數(shù),而不是原來的?Configuration 屬性。

    protected virtual void ConfigureServices(List<IAbpModuleDescriptor> modules, IServiceCollection services)
    {

    var context = new ServiceConfigurationContext(services);
    services.AddSingleton(context);

    foreach (var module in modules)
    {
    if (module.Instance is AbpModule abpModule)
    {
    abpModule.ServiceConfigurationContext = context;
    }
    }


    foreach (var module in modules.Where(m => m.Instance is IPreConfigureServices))
    {
    ((IPreConfigureServices)module.Instance).PreConfigureServices(context);
    }


    foreach (var module in modules)
    {
    if (module.Instance is AbpModule abpModule)
    {
    if (!abpModule.SkipAutoServiceRegistration)
    {
    services.AddAssembly(module.Type.Assembly);
    }
    }

    module.Instance.ConfigureServices(context);
    }


    foreach (var module in modules.Where(m => m.Instance is IPostConfigureServices))
    {
    ((IPostConfigureServices)module.Instance).PostConfigureServices(context);
    }


    foreach (var module in modules)
    {
    if (module.Instance is AbpModule abpModule)
    {
    abpModule.ServiceConfigurationContext = null;
    }
    }
    }

    以上動作都是在?Startup?類當中的?ConfigureService() 方法中執(zhí)行,你可能會奇怪,剩下的四個應用程序生命周期的方法在哪兒執(zhí)行的呢?

    這幾個方法是被抽象成了?IModuleLifecycleContributor?類型,在前面的?AddCoreAbpService()方法的內(nèi)部就被添加到了配置項里面。

    internal static void AddCoreAbpServices(this IServiceCollection services,
    IAbpApplication abpApplication,
    AbpApplicationCreationOptions applicationCreationOptions
    )
    {


    services.Configure<ModuleLifecycleOptions>(options =>
    {
    options.Contributors.Add<OnPreApplicationInitializationModuleLifecycleContributor>();
    options.Contributors.Add<OnApplicationInitializationModuleLifecycleContributor>();
    options.Contributors.Add<OnPostApplicationInitializationModuleLifecycleContributor>();
    options.Contributors.Add<OnApplicationShutdownModuleLifecycleContributor>();
    });
    }

    執(zhí)行的話,則是在?Startup?類的?Configure()?方法當中,它會調(diào)用?AbpApplicationBase?基類的?InitializeModules()?方法,在該方法內(nèi)部也是遍歷所有的?Contributor (生命周期),再將所有的模塊對應的方法調(diào)用一次而已。

    public void InitializeModules(ApplicationInitializationContext context)
    {
    LogListOfModules();


    foreach (var Contributor in _lifecycleContributors)
    {

    foreach (var module in _moduleContainer.Modules)
    {
    Contributor.Initialize(context, module.Instance);
    }
    }

    _logger.LogInformation("Initialized all modules.");
    }

    這里操作可能有點看不懂,不是說調(diào)用模塊的生命周期方法么,為啥還將實例傳遞給 Contributor 呢?我們找到一個 Contributor 的定義就知道了。

    public class OnApplicationInitializationModuleLifecycleContributor : ModuleLifecycleContributorBase
    {
    public override void Initialize(ApplicationInitializationContext context, IAbpModule module)
    {

    (module as IOnApplicationInitialization)?.OnApplicationInitialization(context);
    }
    }

    這里我認為 Abp vNext 把 Contributor 抽象出來可能是為了后面方便擴展吧,如果你也有自己的看法不妨在評論區(qū)留言。

    三、總結(jié)

    至此,整個模塊系統(tǒng)的解析就結(jié)束了,如果看過 Abp 框架源碼解析的朋友就可以很明顯的感覺到,新框架的模塊系統(tǒng)除了生命周期多了幾個以外,其他的變化很少,基本沒太大的變化。

    在 Abp vNext 框架里面,模塊系統(tǒng)是整個框架的基石,了解了模塊系統(tǒng)以后,對于剩下的設計就很好理解了。

    原文地址:https://www.cnblogs.com/myzony/p/10734357.html

    .NET社區(qū)新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

    總結(jié)

    以上是生活随笔為你收集整理的[Abp vNext 源码分析] - 2. 模块系统的变化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。