【spring】如何解决循环依赖

简介: 【spring】如何解决循环依赖

概念

Spring循环依赖是指两个或多个Bean之间相互依赖,形成了双向依赖关系,导致Spring无法正确地完成Bean的创建和初始化。

Spring框架为了解决循环依赖问题,采用了三级缓存的方式来解决。

第一级缓存:单例池中的三级缓存

每个Bean在被创建时,会先放入单例池中的一级缓存(singletonObjects),如果这个Bean被其他的Bean依赖,那么会先从一级缓存中获取,如果一级缓存中还没有,那么就会继续创建。

第二级缓存:提前暴露对象解决循环依赖

如果一个Bean被创建出来,但是其中某个属性引用的是当前Bean类型的另一个Bean,这时候Spring会将当前Bean提前暴露,放入到单例池中的二级缓存(earlySingletonObjects)中,这样在创建该属性引用的Bean时,就可以直接从二级缓存中获取,避免了创建中的循环依赖问题。

第三级缓存:解决循环依赖

如果Spring在从一级和二级缓存中获取Bean时还是出现了循环依赖,那么Spring就会放弃从缓存中获取Bean,而创建一个新的Bean,并将其放入到单例池中的三级缓存(singletonFactories)中,这个Bean创建完成后,Spring会对它进行属性填充,并将其放入到一级缓存中,最终完成Bean的创建和初始化。

需要注意的是,循环依赖是一个比较容易出现的问题,虽然Spring采用了三级缓存的方式解决了这个问题,但在实际项目中还是要尽可能避免循环依赖的出现。

报错

binanceAggTradeListener defined in file [F:\git\eladmin\quantify-op-be\quantify-op-be\quantify-system\target\classes\me\zhengjie\listener\BinanceAggTradeListener.class]

orderRecordServiceImpl

┌─────┐

| commonServiceImpl

↑ ↓

| copyTradingServiceImpl

└─────┘

分析

因为有3个服务的代码一致,所以抽取了一个公共方法,公共方法中也有需要调用其中一个服务的方法,所以就出现了循环依赖

方案

  1. 使用 setter/field 方法注入
    上面说到,只有构造方法是在上下文加载时就要求被注入,容易出现依赖循环。所以可以用其他的方式进行依赖注入,setter 和 field 方法注入与构造方法不同,它们不会在创Bean时就注入依赖,而是在被需要时才注入。
private CopyTradingService copyTradingService;

2.解决Spring 循环依赖的一个简单方法就是对一个Bean使用延时加载。也就是说:这个Bean并没有完全的初始化完,实际上他注入的是一个代理,只有当他首次被使用的时候才会被完全的初始化

  @Autowired
    public CommonServiceImpl(@Lazy CopyTradingService copyTradingService) {
        this.copyTradingService= copyTradingService;
    }

3.在你注入bean时,在互相依赖的两个bean上加上@Lazy注解也可以

@Autowired     
 
@Lazy      
 
private ClassA classA; 
 
 
@Autowired 
 
@Lazy      
 
private ClassB classB; 

总结

循环依赖是Spring框架中一个常见的问题,它发生在两个或更多的bean相互依赖,导致Spring无法正确地创建它们。解决循环依赖的问题需要理解Spring的依赖注入机制以及bean的生命周期。

首先,Spring的依赖注入有两种方式:构造器注入和属性注入。构造器注入是将依赖对象作为构造函数的参数传递给bean,而属性注入则是将依赖对象赋值给bean的属性。当两个bean相互依赖时,构造器注入更容易解决循环依赖的问题,因为可以在构造对象时完成依赖的注入。

解决循环依赖的几种方法:

使用构造器注入:通过将依赖对象作为构造函数的参数传递给bean,可以避免属性依赖导致的循环引用。

使用setter注入:如果不能修改源代码,可以使用setter注入方式,将依赖对象赋值给bean的属性。但是这种方式容易产生循环依赖的问题,因为setter方法是在对象创建后才被调用。

使用@Lazy注解:Spring提供了@Lazy注解,可以将依赖对象的创建延迟到真正使用时。这样可以在一定程度上解决循环依赖的问题,但会增加系统的复杂性和性能开销。

使用接口:如果两个bean之间存在循环依赖,可以尝试将它们抽象为一个接口,通过接口进行通信。这样可以避免直接依赖具体类导致的循环引用问题。

重构代码:从设计层面解决循环依赖的问题是最理想的方案。可以考虑将循环依赖的部分提取出来,作为一个独立的类或者服务,从而消除原始代码中的循环依赖。

总之,解决循环依赖的问题需要结合具体的业务场景和代码结构,选择合适的方法进行优化。同时,要注意代码的可读性和可维护性,避免过度设计导致代码复杂度增加。

目录
相关文章
|
4天前
|
人工智能 Java Spring
Spring Boot循环依赖的症状和解决方案
Spring Boot循环依赖的症状和解决方案
|
4天前
|
存储 缓存 Java
【Spring系列笔记】依赖注入,循环依赖以及三级缓存
依赖注入: 是指通过外部配置,将依赖关系注入到对象中。依赖注入有四种主要方式:构造器注入、setter方法注入、接口注入以及注解注入。其中注解注入在开发中最为常见,因为其使用便捷以及可维护性强;构造器注入为官方推荐,可注入不可变对象以及解决循环依赖问题。本文基于依赖注入方式引出循环依赖以及三层缓存的底层原理,以及代码的实现方式。
24 0
|
4天前
|
存储 缓存 Java
【spring】06 循环依赖的分析与解决
【spring】06 循环依赖的分析与解决
9 1
|
4天前
|
存储 缓存 Java
Spring解决循环依赖
Spring解决循环依赖
|
存储 缓存 Java
Spring 动态代理时是如何解决循环依赖的?为什么要使用三级缓存?
在研究 『 Spring 是如何解决循环依赖的 』 的时候,了解到 Spring 是借助三级缓存来解决循环依赖的。
408 0
|
8月前
|
存储 缓存 Java
Spring为何需要三级缓存解决循环依赖,而不是二级缓存?
今天给大家分享一道大厂面试真题,Spring为何需要三级缓存解决循环依赖,而不是二级缓存?我一共分为五个部分来给大家介绍: 1、什么是循环依赖? 循环依赖就是指循环引用,是两个或多个Bean相互之间的持有对方的引用。在代码中,如果将两个或多个Bean互相之间持有对方的引用,因为Spring中加入了依赖注入机制,也就是自动给属性赋值。Spring给属性赋值时,将会导致死循环。那么,哪些情况会出现循环依赖呢?
158 0
|
9月前
|
缓存 Java Spring
最通俗的方式理解Spring循环依赖三级缓存
有位粉丝找我,说要耽误我5分钟时间,想让我帮助它理解一下Spring循环依赖的三级缓存,绕晕了一个星期,没有想明白。我想今天,用最通俗易懂的方式给大家重新梳理一下,保证让你听懂了。
81 0
|
缓存 Java Spring
Spring的循环依赖和三级缓存
Spring的循环依赖和三级缓存
811 0
Spring的循环依赖和三级缓存
|
缓存 Java Spring
Spring 源码阅读 21:循环依赖和三级缓存
本文分析了 Spring 容器从缓存中获取单例 Bean 的原理,主要涉及到了 Spring 为解决循环依赖问题,而设计的三级缓存机制。
140 0
Spring 源码阅读 21:循环依赖和三级缓存
|
缓存 Java Spring
Spring 为何需要三级缓存解决循环依赖,而不是二级缓存?(2)
Spring 为何需要三级缓存解决循环依赖,而不是二级缓存?(2)
194 1
Spring 为何需要三级缓存解决循环依赖,而不是二级缓存?(2)