已解决:Spring中的循环依赖问题

错误概述

        在7.10号一个阳光明媚的清晨,G先生点了一杯咖啡心情愉悦的来到工位,正准备发挥新的一天的牛马精神。

        同往常一样,开始pull代码 -> Clean Maven -> Install Maven,因为项目中人员较多,大家都统一前一天下班前进行 code merge,所以G先生也养成了第二天检查code merge冲突的习惯。

    果然,不想看到的事情还是发生了....

Description:The dependencies of some of the beans in the application context form a cycle:   classInfoController   classInfoServiceImpl┌─────┐|  departmentQuery└─────┘Action:Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

附图:

原因

  • 排查冲突Bean,

    • 从上图中可以清晰看到,我们主要发生冲突的Bean在departmentQuery中

    • 果然就在这个Bean中发现了奇怪的Bean注入

源码​

  /**   * 通过class获取Bean   *   * @param <T>   Bean类型   * @param clazz Bean类   * @return Bean对象   */  public static <T> T getBean(Class<T> clazz) {    return getBeanFactory().getBean(clazz);  }    这里看到 getBeanFactory().getBean()就真相大白了吧。  通俗一点说:其实就是人家spring在初始化的时候已经将所需要的Bean加载到内存中去了,  而你在其他Bean中又手动去找人家要这个对象,这个时候spring发现已经给你初始化了,你为什么还要给我要呢?  所以spring就给你抛出了BeanCurrentlyInCreationException    这里感兴趣的可以去了解一下spring的三级缓存是如何解决循环依赖

场景分析

参考文献:https://domain-driven-design.org/

  • 这里我们是用@component已经将Bean托付给了Spring

  • 而B先生又采用了反射的方式,去实例化Bean

        @PostConstruct    public void init() {        majorService = SpringUtil.getBean(MajorService.class);    }
  • 我们假设A先生 已在C类文件中通过@Autowired | @Resource 注入过此Bean

  • 这时候B先生又在C类文件中调用了自己写的这个重写方法,又将此Bean注入了一遍

  • 到这里 大家是不是感觉有些别扭了,没错,这就是导致错误的根本原因!

  • 最根本的原因就是 两个类相互引用对方,导致Spring在初始化bean的时候不知道先初始化哪 个,从而形成循环依赖注入。

    注①:从SpringBoot 2.6开始默认禁用了循环依赖

    这里不做过多赘述,对此不理解的自行百度一下Spring的三级缓存,了解一下Bean的初始化过程

    参考文献:Spring中的循环依赖问题以及如何解决的笔记总结_the dependencies of some of the beans in the appli-CSDN博客

    注②:如果这里有人思考 Service实现层为什么不用@service;恰好你是一名三年以上IT开发人员,建议去了解一下 领域驱动设计 相信你对架构设计会有新的见解

解决方案

  • 方案一:

    报错信息已经足够明显,从根本上解决这个问题,不要让它们相互依赖,重新设计代码结构,代码解耦肯定是最优解!如果项目组中有 code review ,这一方案必须优先采取。

  • 方案二

    添加@Lazy注解,如果有参与秒杀、支付相关逻辑,这个注解慎用!

     package com.example.demo.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service; @Servicepublic class AService {    @Lazy    @Autowired    private BService bService;}
  • 方案三

    在application.properties配置当中加入

    spring.main.allow-circular-references: true

    注:此开关主要处理json序列化与反序列化时,循环引用问题,不建议打开,很容易吞异常,在部署的时候容易发生不可知异常。

  • 建议:如果本地调试,不影响团队的情况下,三种方案皆可采用;但是在 pull code 前建议采用第一种方案,处理完依赖后在进行Submit

追风筝的人

我成为今天的我,是在1975年某个阴云密布的寒冷冬日,那年我12岁。

我清楚的记得当时趴在一堵坍塌的泥墙后面,窥视着那条小巷,旁边是结冰的小溪。

许多年过去了,人们说陈年旧事可以被埋葬,然而我终于明白这是错的,因为往事会自行爬上来。

回首前尘,我意识到在过去二十六年里,自己始终在窥视着那荒芜的小径。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值