ABP 依赖注入源码解读:约定注册器是如何自动扫描和注册服务的 📅 2026/8/2 3:28:29 传统 vs ABP传统方式services.AddScopedIUserService,UserService();services.AddTransientIEmailService,EmailService();ABP 方式publicclassUserService:IUserService,ITransientDependency{}背后的原理是什么直接看DefaultConventionalRegistrar.cs源码。源码位置framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DefaultConventionalRegistrar.cs约定注册的核心流程publicoverridevoidAddType(IServiceCollectionservices,Typetype){// 1. 检查是否禁用了约定注册if(IsConventionalRegistrationDisabled(type))return;// 2. 获取生命周期vardependencyAttributeGetDependencyAttributeOrNull(type);varlifeTimeGetLifeTimeOrNull(type,dependencyAttribute);if(lifeTimenull)return;// 没实现标记接口不注册// 3. 获取暴露的服务类型注册为哪个接口varexposedServiceAndKeyedServiceTypesGetExposedKeyedServiceTypes(type).Concat(GetExposedServiceTypes(type).Select(tnewServiceIdentifier(t))).ToList();// 4. 触发暴露事件其他模块可以修改TriggerServiceExposing(services,type,exposedServiceAndKeyedServiceTypes);// 5. 逐一添加到 DI 容器foreach(varexposedServiceTypeinexposedServiceAndKeyedServiceTypes){varserviceDescriptorCreateServiceDescriptor(type,...,lifeTime);services.Add(serviceDescriptor);}}第一步生命周期判定GetLifeTimeOrNull的优先级protectedvirtualServiceLifetime?GetLifeTimeOrNull(Typetype,DependencyAttribute?dependencyAttribute){returndependencyAttribute?.Lifetime// 1. [Dependency] 属性指定??GetServiceLifetimeFromClassHierarchy(type)// 2. 标记接口??GetDefaultLifeTimeOrNull(type);// 3. 默认值null 不注册}GetServiceLifetimeFromClassHierarchy检查类的继承链if(typeof(ITransientDependency).IsAssignableFrom(type))returnServiceLifetime.Transient;if(typeof(ISingletonDependency).IsAssignableFrom(type))returnServiceLifetime.Singleton;if(typeof(IScopedDependency).IsAssignableFrom(type))returnServiceLifetime.Scoped;三个标记接口都是空接口没有任何方法纯标记用途publicinterfaceITransientDependency{}// 空接口publicinterfaceISingletonDependency{}// 空接口publicinterfaceIScopedDependency{}// 空接口第二步暴露服务类型判定ExposedServiceExplorer.GetExposedServices的默认逻辑// 1. 有 [ExposeServices] → 用属性指定的类型// 2. 没有 → 按约定推断// 遍历类实现的所有接口// 接口名去掉 I 前缀剩下的跟类名比较// 如果类名以它结尾不区分大小写则匹配比如publicclassProductService:IProductService,ITransientDependency// IProductService → 去掉 I → ProductService → 类名 ProductService 以 ProductService 结尾 → 匹配// 相当于 [ExposeServices(typeof(IProductService))]第三步ServiceDesciptor 注册最后调用services.Add(serviceDescriptor)但还有两个变体if(dependencyAttribute?.ReplaceServicestrue)services.Replace(serviceDescriptor);// 替换已有注册elseif(dependencyAttribute?.TryRegistertrue)services.TryAdd(serviceDescriptor);// 不存在时才注册elseservices.Add(serviceDescriptor);// 默认直接注册启动时扫描谁ABP 启动时每个模块的ConfigureServices阶段会调用context.Services.AddAssemblyOfTModule();这个方法扫描当前模块所在程序集的所有公开类型对每个类型执行DefaultConventionalRegistrar.AddType。所以一个类只要在模块的程序集中、实现了标记接口就会被自动注册。实战如何排查注册问题如果发现IServiceProvider.GetServiceT()返回 null按这个顺序排查检查类是否在模块的程序集中在哪个项目那个项目被引用了检查类是否实现了标记接口ITransientDependency/ISingletonDependency/IScopedDependency检查是否写了[DisableConventionalRegistration]检查生命周期选择是否正确对比总结// 方式 1标记接口最常用publicclassMyService:ITransientDependency{}// 方式 2标记接口 显示的暴露接口[ExposeServices(typeof(IUserService))]publicclassUserService:IUserService,ITransientDependency{}// 方式 3[Dependency] 精细控制[Dependency(ReplaceServicestrue)]publicclassMockService:IRealService,ITransientDependency{}// 方式 4禁用自动注册手动管理[DisableConventionalRegistration]publicclassHelperService{}// 需要手动 services.AddSingletonHelperService()