天天看點

APB VNext系列(一):依賴注入

兄弟姐妹們,還有三個月就2021年了,給大家提前拜個早年~嘻嘻。

本系列我将梳理ABP VNext中的技術實作,在自我記錄的同時,也幫大家更加深入了解下這個最近非常火的架構。

什麼是ABP VNext這裡就不做介紹了,大家可以去看資料了解下。這裡講解我們實際業務中如何使用該架構。

1.簡介

   ABP中依賴注入是基于Microsoft.Extensions.DependencyInjection實作的,在元件開發過程中其實作方式和包括AutoFac在内的第三方類庫大同小異。

  在實際的開發使用中,我們并沒有使用第三方類庫,完全基于Microsoft的依賴注入擴充庫進行的元件開發。

2.如何實作依賴注入?

我們先來看下傳統的實作方式:

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddTransient<ITestApiService, TestApiService>();
        }      

可以看到我們需要使用IServiceCollection這個屬性,讓我們來看下IServiceCollection的内部實作:

public class ServiceCollection : IServiceCollection
{
    private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();

    public int Count => _descriptors.Count;

    public bool IsReadOnly => false;

    public ServiceDescriptor this[int index]
    {
        get
        {
            return _descriptors[index];
        }
        set
        {
            _descriptors[index] = value;
        }
    }

    // ...
}      

可以看到

IServiceCollection

沒有定義其任何成員,而是從

IList<ServiceDescriptor>

派生,本質上是一個集合。

在實際生産過程中,我們肯定不能一個個的手動去注冊,而是希望通過某種約定配置讓其實作自動注冊。

2.如何自定義實作一個依賴注入元件?

首先我們要建立三個接口,分别是L:ITransientDependency、ISingletonDependency、IScopedDependency,分别對應三種生命周期:

public interface ITransientDependency
    {

    }    

    public interface ISingletonDependency
    {

    }

    public interface IScopedDependency
    {

    }      

所有需要實作依賴注入的類都需要繼承這其中的某個接口。

然後我們建立一個接口以及實作類:

public interface ITestApiService
    {
        public string GetResult();
    }


    public class TestApiService : ITestApiService, ITransientDependency
    {
        public string GetResult()
        {
            return "This is test!";
        }
    }      

我們還需要建立一個接口IApplication,用來擷取程式集。(也可以通過其他方式,隻不過這個IApplication後面其他功能會用到)

public interface IApplication
    {

    }      

然後下面是完整實作:

public void DependencyRegister(IServiceCollection services)
        {
            var assembly = typeof(IApplication).GetTypeInfo().Assembly;
            var types = assembly.GetTypes()
                .Where(type => type != null
                && type.IsClass
                && !type.IsAbstract
                && !type.IsGenericType).ToArray();

            foreach (var type in types)
            {
                ServiceLifetime? lifeTime = null;

                if (typeof(ITransientDependency).GetTypeInfo().IsAssignableFrom(type))
                {
                    lifeTime = ServiceLifetime.Transient;
                }

                if (typeof(ITransientDependency).GetTypeInfo().IsAssignableFrom(type))
                {
                    lifeTime = ServiceLifetime.Transient;
                }

                if (typeof(ITransientDependency).GetTypeInfo().IsAssignableFrom(type))
                {
                    lifeTime = ServiceLifetime.Transient;
                }

                if (!lifeTime.HasValue)
                    continue;

                var serviceTypes = type
                .GetCustomAttributes()
                .OfType<IExposedServiceTypesProvider>()
                .DefaultIfEmpty(new ExposeServicesAttribute
                {
                    IncludeDefaults = true,
                    IncludeSelf = true
                })
                .SelectMany(p => p.GetExposedServiceTypes(type))
                .ToList();

                foreach (var serviceType in serviceTypes)
                {
                    var serviceDescriptor = ServiceDescriptor.Describe(serviceType, type, lifeTime.Value);
                    services.Add(serviceDescriptor);
                }
            }
        }      
public interface IExposedServiceTypesProvider
    {
        Type[] GetExposedServiceTypes(Type targetType);
    }

    public class ExposeServicesAttribute : Attribute, IExposedServiceTypesProvider
    {
        public Type[] ServiceTypes { get; }

        public bool? IncludeDefaults { get; set; }

        public bool? IncludeSelf { get; set; }

        public ExposeServicesAttribute(params Type[] serviceTypes)
        {
            ServiceTypes = serviceTypes ?? new Type[0];
        }

        public Type[] GetExposedServiceTypes(Type targetType)
        {
            var serviceList = ServiceTypes.ToList();

            if (IncludeDefaults == true)
            {
                foreach (var type in GetDefaultServices(targetType))
                {
                    serviceList.AddIfNotContains(type);
                }

                if (IncludeSelf != false)
                {
                    serviceList.AddIfNotContains(targetType);
                }
            }
            else if (IncludeSelf == true)
            {
                serviceList.AddIfNotContains(targetType);
            }

            return serviceList.ToArray();
        }

        private static List<Type> GetDefaultServices(Type type)
        {
            var serviceTypes = new List<Type>();

            foreach (var interfaceType in type.GetTypeInfo().GetInterfaces())
            {
                var interfaceName = interfaceType.Name;

                if (interfaceName.StartsWith("I"))
                {
                    interfaceName = interfaceName.Right(interfaceName.Length - 1);
                }

                if (type.Name.EndsWith(interfaceName))
                {
                    serviceTypes.Add(interfaceType);
                }
            }

            return serviceTypes;
        }
    }      

首先我們通過IApplication用反射的方式擷取到程式集中所有需要被檢查的類,然後通過判斷其繼承的接口類型決定需要實作的生命周期。

這裡的預設規則是接口定義層的名稱去掉“I”字元後與實作層類相同,則表明是需要注冊的。

看完是不是突然覺得挺簡單的了!

繼續閱讀