依赖注入实现类循环引用导致错误
一次拉取代码之后编译、启动未出现错误,服务启动后调用API报错,错误信息如下:
.......
An unandled exception has occurred while executing the request.
Autofac.Core.DependencyResolutionException: An exception was thrown while activating
UOM.Service.DomainService.MachineDomainService ->
UOM.Service.DomainService.ServiceNodeDomainService ->
UOM.Service.DomainService.ServiceDomainService.
---> Autofac.Core.DependencyResolutionException: Circular component dependency
detected: UOM.Service.DomainService.MachineDomainService ->
UOM.Service.DomainService.ServiceNodeDomainService ->
UOM.Service.DomainService.ServiceDomainService ->
UOM.Service.DomainService.ServiceNodeDomainService.
at Autofac.Core.Resolving.CircularDependencyDetector.CheckForCircularDependency(IComponentRegistration registration, Stack`1 activationStack, Int32 callDepth)
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Activators.Reflection.AutowiringParameter.<>c__DisplayClass0_0.<CanSupplyValue>b__0()
at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()
at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters, Object& decoratorTarget)
--- End of inner exception stack trace ---
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters, Object& decoratorTarget)
at Autofac.Core.Resolving.InstanceLookup.Execute()
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.ResolveOperation.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
......
错误分析
错误信息解读
Autofac.Core.DependencyResolutionException: An exception was thrown while activating
翻译为激活时抛出异常,意思是无法创建接口的实例Autofac.Core.DependencyResolutionException: Circular component dependency
翻译为组件形成循环引用
寻找循环
UOM.Service.DomainService.MachineDemainService ->
UOM.Service.DomainService.ServiceNodeDomainService ->
UOM.Service.DomainService.ServiceDomainService ->
UOM.Service.DomainService.ServiceNodeDomainService.
编号
① MachineDomainService
② ServiceNodeDomainService
③ ServiceDomainService
查看服务代码后的确是存在循环引用
反思
在此次事件中,由于没有进行评估和详细设计,直接进行编码,从而没有把类的职责划分清楚,所以导致循环引用。实际上可以设置允许循环引用
builder.RegisterType<UserService>()
.InstancePerLifetimeScope()
.PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
这种处理方式只会导致问题变得越来越糟糕,应当解除循环引用和减少耦合度,提高代码的可维护性。如果项目非常紧急可以这样在处理(给自己挖坑)
在实际工作容易出现循环依赖的场景有:
- 同一个工程不同模块依赖注入;
- 多个过程进行项目;
- 多个服务进行通信(Http、MQ、Rpc)等。
保持类、模块、服务的自治性,尽量不要形成依赖。单体服务设计时同一层次不要直接引用,尽量创建自身能够控制的实体,调用方(高层级)将需要依赖的转为该实体传入,降低耦合度。微服务中尽量少用Rpc,避免滥用,如果非要用也必须统一管理。服务数量过多时,是否存在循环依赖则需要进行整体结构的宏观把握,理清楚服务间的关系,进行良好的服务划分,怎么划分服务则需要根据具体业务分析和经验了。