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”字符后與實現層類相同,則表明是需要注冊的。

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM