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”字符后与实现层类相同,则表明是需要注册的。
看完是不是突然觉得挺简单的了!