兄弟姐妹們,還有三個月就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”字符后與實現層類相同,則表明是需要注冊的。
看完是不是突然覺得挺簡單的了!
