Spring中的循环依赖问题以及如何解决的笔记总结

本文详细介绍了Spring中遇到的循环依赖问题,通过一个具体的例子展示了AService和BService之间的循环依赖导致的错误。分析了问题的根本原因,并提出了三种解决方案:重构代码以避免循环依赖、使用`@Lazy`注解延迟加载和配置Spring允许循环依赖。最后,深入探讨了Spring如何通过三级缓存机制解决单例Bean的循环依赖问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:在最近的工作当中偶然遇到了Spring的循环依赖的Bug,搜集了网上的资料参考和分析,写了一份总结进行学习和分享!

目录

一、错误概述

二、根本原因

三、解决方案

四、Spring是如何解决循环依赖的


一、错误概述

在最近的工作当中,启动项目的时候报了一个类似于以下的错误:

Description:

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

┌─────┐

|  AService (field private com.example.demo.service.BService

com.example.demo.service.AService.bService)

↑     ↓

|  BService (field private com.example.demo.service.AService

com.example.demo.service.BService.aService)

└─────┘

二、根本原因

在AService 注入了BService这个Bean,在BService 同时也注入了 AService 这个Bean,两个类相互引用对方,导致Spring在初始化bean的时候不知道先初始化哪 个,从而形成循环依赖注入。详细如下:

AService:

package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AService {
    @Autowired
    private BService bService;
}

BService:

package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BService {
    @Autowired
    private AService aService;
}

三、解决方案

1.从根本上解决这个问题,不要让它们相互依赖,重新设计代码结构,代码解耦肯定是最优解!

2.添加@Lazy注解


package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AService {
    @Lazy
    @Autowired
    private BService bService;
}
package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BService {
    @Lazy
    @Autowired
    private AService aService;
}

这个注解的原理是:当实例化对象时,如果发现参数或者属性有@Lazy注解修饰,则不会直接创建所依赖的对象,而是使用动态代理创建一个代理类 通俗的来说:AService的创建,需要依赖BService,那就不直接创建BService了,而是使用动态代理创建了一个代理类BService1,此时AService和BService就不是相互依赖了,从而变成了AService依赖了一 个代理类BService1,BService依赖AService。但因为在注入依赖时,AService并没有完全初始化结束, 实际上注入了一个代理对象,只有他当首次被使用的时候才会被完全初始化。

3.在application.properties配置当中加入

spring.main.allow-circular-references=true

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

四、Spring是如何解决循环依赖的

单例Bean的初始化需要经历三步:

Bean初始化步骤:

注入就发生在第二步,属性赋值,结合这个过程,Spring通过三级缓存解决了循环依赖:

一级缓存 : Map<String,Object> singletonObjects,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例。

二级缓存 : Map<String,Object> earlySingletonObjects,早期曝光对象,用于保存实例化完成的 bean实例。

三级缓存 : Map<String,ObjectFactory<?>> singletonFactories,早期曝光对象工厂,用于保存 bean 创 建工厂,以便于后面扩展有机会。

当 A、B 两个类发生循环依赖时: 

A实例的初始化过程:

1.创建A实例,实例化的时候把A对象工厂放入三级缓存,表示A开始实例化了,虽然我这个对象还不完整,但是先曝光出来让大家知道。

2.A注入属性时,发现依赖B,此时B还没有被创建出来,所以去实例化B。

3.同样,B注入属性时发现依赖A,它就会从缓存里找A对象。依次从一级到三级缓存查询A,从三级缓存通过对象工厂拿到A,发现A虽然不太完善,但是存在,把A放入二级缓存,同时删除三级缓存中的A,此时,B已经实例化并且初始化完成,把B放入一级缓存。

4.接着A继续属性赋值,顺利从一级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除二级缓存中的A,同时把A放入一级缓存。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黄团团

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值