一、简要说明
ABP vNext 框架在使用依赖注入服务的时候,是直接使用的微软提供的 Microsoft.Extensions.DependencyInjection 包。这里与原来的 ABP 框架就不一样了,原来的 ABP 框架还需要抽象出来一个 IIocManager
用来管理整个 IoC 容器,现在则直接操作 IServiceCollection
与 IServiceProvider
进行组件的注册/解析。
这里需要注意的是,虽然现在的依赖注入服务是使用微软官方那一套库进行操作,但是 ABP vNext 还是为我们提供了组件自动注册、拦截器这些基础功能。
二、源码分析
2.1 组件自动注册
ABP vNext 仍然在其 Core 库为我们提供了三种接口,即 ISingletonDependency
和 ITransientDependency
、IScopedDependency
接口,方便我们的类型/组件自动注册,这三种接口分别对应了对象的 单例、瞬时、范围 生命周期。只要任何类型/接口实现了以上任意接口,ABP vNext 就会在系统启动时候,将这些对象注册到 IoC 容器当中。
那么究竟是在什么时候呢?回顾上一章的模块系统的文章,在模块系统调用模块的 ConfigureService()
的时候,就会有一个 services.AddAssembly(module.Type.Assembly)
,他会将模块的所属的程序集传入。
public class ModuleLoader : IModuleLoader
{
// ... 其他代码
protected virtual void ConfigureServices(List<IAbpModuleDescriptor> modules, IServiceCollection services)
{
// ... 其他代码
//ConfigureServices
foreach (var module in modules)
{
if (module.Instance is AbpModule abpModule)
{
// 是否跳过服务的自动注册,默认为 false。
if (!abpModule.SkipAutoServiceRegistration)
{
services.AddAssembly(module.Type.Assembly);
}
}
module.Instance.ConfigureServices(context);
}
// ... 其他代码
}
// ... 其他代码
}
看来核心就在于这个 AddAssembly()
扩展方法了,跳转到方法的内部,发现真正干事的是 IConventionalRegistrar
对象,暂且称之为规约注册器,而且我们可以拥有多个规约注册器,你可以自己实现自动注册规则。
public static IServiceCollection AddAssembly(this IServiceCollection services, Assembly assembly)
{
// 获得所有规约注册器,然后调用规约注册器的 AddAssmbly 方法注册类型。
foreach (var registrar in services.GetConventionalRegistrars())
{
registrar.AddAssembly(services, assembly);
}
return services;
}
该接口定义了三个方法,支持传入程序集、类型数组、具体类型,他们的默认实现都在抽象类 ConventionalRegistrarBase
当中。
public interface IConventionalRegistrar
{
void AddAssembly(IServiceCollection services, Assembly assembly);
void AddTypes(IServiceCollection services, params Type[] types);
void AddType(IServiceCollection services, Type type);
}
抽象类当中的实现也非常简单,他们最终都是调用的 AddType()
方法来将类型注册到 IServiceCollection
当中的。
public abstract class ConventionalRegistrarBase : IConventionalRegistrar
{
public virtual void AddAssembly(IServiceCollection services, Assembly assembly)
{
// 获得程序集内的所有类型,过滤掉抽象类和泛型类型。
var types = AssemblyHelper
.GetAllTypes(assembly)
.Where(
type => type != null &&
type.IsClass &&
!type.IsAbstract &&
!type.IsGenericType
).ToArray();
AddTypes(services, types);
}
public virtual void AddTypes(IServiceCollection services, params Type[] types)
{
foreach (var type in types)
{
AddType(services, type);
}
}
public abstract void AddType(IServiceCollection services, Type type);
}
所以我们的重点就在于 AddType()
方法,ABP vNext 框架默认的规约注册器叫做 DefaultConventionalRegistrar
,跳转到其定义可以发现在其内部,除了对三种生命周期接口处理之外,如果类型使用了 DependencyAttribute
特性,也会根据该特性的参数配置进行不同的注册逻辑。
public override void AddType(IServiceCollection services, Type type)
{
// 判断类型是否标注了 DisableConventionalRegistration 特性,如果有标注,则跳过。
if (IsConventionalRegistrationDisabled(type))
{
return;
}
// 获得 Dependency 特性,如果没有则返回 null。
var dependencyAttribute = GetDependencyAttributeOrNull(type);
// 优先使用 Dependency 特性所指定的生命周期,如果不存在则根据 type 实现的接口确定生命周期。
var lifeTime = GetLifeTimeOrNull(type, dependencyAttribute);
if (lifeTime == null)
{
return;
}
// 获得等待注册的类型定义,类型的定义优先使用 ExposeServices 特性指定的类型,如果没有则使用
// 类型当中接口以 I 开始,后面为实现类型名称的接口。
foreach (var serviceType in AutoRe