The dependencies of some of the beans in the application context form a cycle

946 阅读5分钟

你们好,我是金金金。

image.png

场景

image.png

  • 启动服务时,报错:应用程序上下文中的某些bean的依赖关系形成了一个循环

循环依赖

依赖循环指的是两个或多个类之间相互依赖的情况,即类A依赖类B,同时类B也依赖类A。 这种情况会导致编译器无法确定类的加载顺序,从而产生编译错误

image.png

  • 如上图:BeanA 类依赖了 BeanB 类,同时 BeanB 类又依赖了 BeanA 类。这种依赖关系形成了一个闭环,这种依赖关系就称之为循环依赖

排查

从报错信息可以得知,这是一个依赖循环的问题~,问题出在MqConfigrabbitTemplateConfigurer这两者形成了相互依赖,从而产生了依赖循环

  • 从报错得知,整理的依赖顺序如下~
    payController 依赖于 payOrderServiceImpl
    payOrderServiceImpl 依赖于 MqConfig
    MqConfig 依赖于 rabbitTemplate
    rabbitTemplate 依赖于 rabbitTemplateConfigurer
    rabbitTemplateConfigurer 依赖于 MqConfig
  • 好,一步步来,走起~
  1. 我先贴出MqConfigRabbitMqHelper代码

    Mq.config.java image.png RabbitMqHelper.java

image.png

  1. 从报错开始排查,首先看PayController,这里面使用了IPayOrderService那必然有依赖关系

    image.png

  2. 再继续看payOrderServiceImpl

    image.png

    • PayOrderServiceImpl里面使用到了rabbitMqHelper,而rabbitMqHelper又是在MqConfig里面注册为一个Bean的,所以也就是为什么payOrderServiceImpl依赖于MqConfig的原因了~
  3. 继续走,我们看一下MqConfig,其实上面也有贴出MqConfig的详细代码了,我这里再放一遍

    image.png

    • 从报错显示,MqConfig依赖于rabbitTemplate,这很明显了,因为我MqConfig里面有使用到rabbitTemplate,所以也存在着依赖关系
  4. 继续,我们可以看到rabbitTemplate依赖于rabbitTemplateConfigurer,这里得看一下源码了,跟着节奏,走起

    • 我们来到RabbitAutoConfiguration 里面,可以看到三个内部类

      image.png

    • 根据报错得知rabbitTemplate依赖于rabbitTemplateConfigurer,来看下RabbitTemplateConfiguration这个内部类究竟是什么?

      image.png

      • 现在就很明显了,可以看到rabbitTemplate就在RabbitTemplateConfiguration内部类当中,rabbitTemplate在这个内部类当中注册为了Bean,而且这个Bean是依赖于RabbitTemplateConfigurer的,所以也就是为什么rabbitTemplate依赖于rabbitTemplateConfigurer
  5. 好,现在也就是最后一点,rabbitTemplateConfigurer为什么又依赖于MqConfig呢,这两者导致了依赖循环

  • 首先看下rabbitTemplateConfigurer方法,从源码可以得知 参数里面需要一个ObjectProvider messageConverter消息转换器

    image.png

  • 我在MqConfig里面 也自定义了一个消息转换器,问题就出在这里

    image.png

造成error的原因

MqConfig依赖于rabbitTemplate
rabbitTemplate的自动注入需要有MessageConverter
而我又在MqConfig自定义了MessageConverter
MqConfig需要rabbitTemplate,由于我定义了消息转换器,而rabbitTemplate又会使用到MqConfig, 反反复复,就形成了循环依赖

解决

  • 知道了错误的原因,解决起来就很简单了,就是因为 MqConfig需要rabbitTemplate,由于我定义了消息转换器,而rabbitTemplate又会使用到MqConfig 这个问题所导致循环依赖。

第一种解决方案

  • 把消息转换器放到别的配置类中去,这样就可以避免rabbitTemplate去找MqConfig

image.png

第二种解决方案

  • 要么就把MqConfig类中的rabbitTemplate以及init方法拿走,直接切断关系使MqConfig 不依赖于 rabbitTemplate,我选择放在我的RabbitMqHelper类当中

image.png

测试

  • 完美启动,不报错了~

image.png

总结

依赖循环导致的问题,切断两者互相依赖关系即可~

  • 编写有误还请大佬指正,万分感谢。