[Abp 源碼分析]3、依賴注入

0.簡要介紹

在 Abp 框架裏面,無時無刻不存在依賴注入,關於依賴注入的做用與好處我就不在這裏多加贅述了,網上有不少解釋的教程。在 [Abp 源碼分析]1、Abp 框架啓動流程分析 裏面已經說過,Abp 自己在框架初始化的時候咱們就能夠看到它使用 Castle Windsor 將 Asp.Net Core 自帶的 IServiceProvider 替換掉了。html

1.大致結構

在 Abp 框架當中,它的依賴注入相關的類型基本上都放在 Abp 項目的 Dependency 文件夾裏面,下圖是他們之間的依賴關係:app

僞裝有圖

2 代碼解析

2.1 基本實現

IIocManager 是直接繼承 IIocRegistrarIIocResolver 的一個接口,經過名稱咱們就能夠看出來他們的做用,IIocRegistrar 內部提供了組件註冊的方法定義,而 IIocResolver 內部則是提供瞭解析已經注入的組件方法。在 IIocManager 自己則是封裝了一個 Castle Windsor 的 Ioc 容器,定義以下:框架

/// <summary>
/// This interface is used to directly perform dependency injection tasks.
/// </summary>
public interface IIocManager : IIocRegistrar, IIocResolver, IDisposable
{
    /// <summary>
    /// Reference to the Castle Windsor Container.
    /// </summary>
    IWindsorContainer IocContainer { get; }

    /// <summary>
    /// Checks whether given type is registered before.
    /// </summary>
    /// <param name="type">Type to check</param>
    new bool IsRegistered(Type type);

    /// <summary>
    /// Checks whether given type is registered before.
    /// </summary>
    /// <typeparam name="T">Type to check</typeparam>
    new bool IsRegistered<T>();
}

那麼咱們來看看 IIocManager 的具體實現。ide

方法雖然看起來挺多,不過更多的只是在 Castle Windsor 上面進行了一層封裝而已,能夠看到 Register() 這個註冊方法在其內部也是直接調用的 IWindsorContainer.Register() 來進行注入。函數

那麼 Abp 爲何還要再包裝一層呢,由於對外開放的你在使用的時候都使用的是 IIocManager 提供的註冊方法,那麼你須要替換 DI 框架的時候能夠很快捷的替換掉整個依賴注入框架而不會影響現有代碼。源碼分析

public void Register(Type type, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton)
{
    IocContainer.Register(ApplyLifestyle(Component.For(type), lifeStyle));
}

2.2 規約注入

咱們重點說一說它的規約注入,什麼是規約注入?this

在上面的類圖當中,能夠看到有一個 IConventionalDependencyRegistrar 接口,而且該接口還擁有四個實現,咱們以 BasicConventionalRegistrar 類爲例子看看裏面作了什麼操做。code

/// <summary>
/// This class is used to register basic dependency implementations such as <see cref="ITransientDependency"/> and <see cref="ISingletonDependency"/>.
/// </summary>
public class BasicConventionalRegistrar : IConventionalDependencyRegistrar
{
    public void RegisterAssembly(IConventionalRegistrationContext context)
    {
        //Transient
        context.IocManager.IocContainer.Register(
            Classes.FromAssembly(context.Assembly)
                .IncludeNonPublicTypes()
                .BasedOn<ITransientDependency>()
                .If(type => !type.GetTypeInfo().IsGenericTypeDefinition)
                .WithService.Self()
                .WithService.DefaultInterfaces()
                .LifestyleTransient()
            );

        //Singleton
        context.IocManager.IocContainer.Register(
            Classes.FromAssembly(context.Assembly)
                .IncludeNonPublicTypes()
                .BasedOn<ISingletonDependency>()
                .If(type => !type.GetTypeInfo().IsGenericTypeDefinition)
                .WithService.Self()
                .WithService.DefaultInterfaces()
                .LifestyleSingleton()
            );

        //Windsor Interceptors
        context.IocManager.IocContainer.Register(
            Classes.FromAssembly(context.Assembly)
                .IncludeNonPublicTypes()
                .BasedOn<IInterceptor>()
                .If(type => !type.GetTypeInfo().IsGenericTypeDefinition)
                .WithService.Self()
                .LifestyleTransient()
            );
    }
}

BasicConventionalRegistrar 內部,他會掃描傳入的程序集,而且根據類型所繼承的接口來進行自動注入,因此 Abp 定義了兩個輔助注入接口,叫作ITransientDependencyISingletonDependency,而且在下面還注入了攔截器。orm

這樣的話,咱們本身就不須要頻繁的使用 IIocManager.Register() 方法來手動注入,只須要在本身的實現類或者接口上面,繼承上述兩個接口之一便可。htm

IocManager 內部維護了一個集合 _conventionalRegistrars

/// <summary>
/// List of all registered conventional registrars.
/// </summary>
private readonly List<IConventionalDependencyRegistrar> _conventionalRegistrars;

這個集合就是已經存在的規約註冊器,在 AbpKernelModule 的預加載方法裏面就使用 AddConventionalRegistrar() 方法來添加了 BasicConventionalRegistrar 註冊器。代碼在 AbpKernelModule.cs 的 45 行:

public override void PreInitialize()
{
    IocManager.AddConventionalRegistrar(new BasicConventionalRegistrar());
    
    // 其餘代碼
}

以後每當程序調用 IIocManager.RegisterAssemblyByConvention(Assembly assembly) 方法的時候,就會根據傳入的 Assembly 來循環調用存放在集合裏面註冊器的 RegisterAssembly() 方法,固然你也能夠隨時定義一個 Registrar ,註冊約定你也能夠本身來編寫。

public void RegisterAssemblyByConvention(Assembly assembly, ConventionalRegistrationConfig config)
{
    var context = new ConventionalRegistrationContext(assembly, this, config);

    foreach (var registerer in _conventionalRegistrars)
    {
        registerer.RegisterAssembly(context);
    }

    if (config.InstallInstallers)
    {
        IocContainer.Install(FromAssembly.Instance(assembly));
    }
}

注:通常來講,每一個模塊都會在它的 Initialize 方法當中調用 IocManager.RegisterAssemblyByConvention(),將本身傳入該方法當中來注入當前模塊程序集全部符合規約的組件。

這裏值得注意的是 RegisterAssemblyByConvention() 方法還有一個重載 RegisterAssemblyByConvention(Assembly assembly, ConventionalRegistrationConfig config),他將會傳入一個 ConventionalRegistrationConfig 對象,該對象只有一個 bool InstallInstallers 屬性,主要是在註冊的時候告訴 Abp 框架是否使用該程序集內部的 IWindsorInstaller 接口規則。

2.3 初始化過程

吶,首先呢在咱們初始化 AbpBootstrapper 的時候,就已經建立好了咱們的 IocManager 實例,咱們能夠來到 AbpBootstrapper.cs 的構造函數有如下代碼:

public IIocManager IocManager { get; }

private AbpBootstrapper([NotNull] Type startupModule, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
{
    Check.NotNull(startupModule, nameof(startupModule));

    var options = new AbpBootstrapperOptions();
    optionsAction?.Invoke(options);

    if (!typeof(AbpModule).GetTypeInfo().IsAssignableFrom(startupModule))
    {
        throw new ArgumentException($"{nameof(startupModule)} should be derived from {nameof(AbpModule)}.");
    }

    StartupModule = startupModule;

    IocManager = options.IocManager;
    PlugInSources = options.PlugInSources;

    _logger = NullLogger.Instance;

    if (!options.DisableAllInterceptors)
    {
        AddInterceptorRegistrars();
    }
}

能夠看到在 new 了一個 AbpBootstrapperOptions 對象,而且在第 17 行將 options 建立好的 IocManager 賦值給 AbpBootstrapper 自己的 IocManager 屬性。

那麼在 options 內部是如何建立 IIocManager 的呢?

public AbpBootstrapperOptions()
{
    IocManager = Abp.Dependency.IocManager.Instance;
    PlugInSources = new PlugInSourceList();
}

能夠看到他直接是使用的 IocManager 這個類所提供的一個靜態實例。

也就是在 IocManager 類裏面他有一個靜態構造函數:

static IocManager()
{
    Instance = new IocManager();
}

就是這種操做,以後在 IocManager 的構造函數裏面呢就將本身再註冊到了 Castle Windsor 的容器裏面,這樣其餘的組件就能夠直接注入使用 IIocManager 了。

public IocManager()
{
    IocContainer = new WindsorContainer();
    _conventionalRegistrars = new List<IConventionalDependencyRegistrar>();

    //Register self!
    IocContainer.Register(
        Component.For<IocManager, IIocManager, IIocRegistrar, IIocResolver>().UsingFactoryMethod(() => this)
        );
}

咱們能夠回顧一下在替換 Asp.Net Core 自身的 Ioc 容器的時候,在使用的 CreateServiceProvider 就是 Castle Windsor 提供的 IocContainer 對象,該對象就是咱們上文在 AbpBootstrapperOptions 裏面建立的靜態實例。

public static IServiceProvider AddAbp<TStartupModule>(this IServiceCollection services, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
    where TStartupModule : AbpModule
{
    var abpBootstrapper = AddAbpBootstrapper<TStartupModule>(services, optionsAction);

    ConfigureAspNetCore(services, abpBootstrapper.IocManager);

    return WindsorRegistrationHelper.CreateServiceProvider(abpBootstrapper.IocManager.IocContainer, services);
}

3.初始化流程圖

總的來講呢,整個 Abp 框架的依賴注入相關的初始化流程圖就像這樣。

4.點此跳轉到總目錄

相關文章
相關標籤/搜索