Spring 怎么解决循环依赖的问题?

Spring框架解决循环依赖主要通过三级缓存来实现,这主要发生在Spring容器创建bean的过程中。以下是Spring解决循环依赖的基本步骤:

  1. 一级缓存(singletonObjects):存放已经创建好的单例对象,供其他bean引用。

  2. 二级缓存(earlySingletonObjects):存放原始的bean对象,即已经填充了属性(依赖注入完成)但尚未经过初始化(调用afterPropertiesSet方法)的bean对象。

  3. 三级缓存(singletonFactories):存放bean工厂对象(BeanFactoryObjectProvider),用于解决循环依赖。当Spring容器创建bean时,如果检测到循环依赖,它会将bean的早期引用(early reference)存入三级缓存中。

具体步骤如下:

  • 创建Bean:当Spring容器创建一个bean时,首先会检查该bean是否已经存在于一级缓存中。如果存在,则直接使用;如果不存在,则继续创建。

  • 注册Bean:在bean创建过程中,Spring会将bean的早期引用(未初始化的bean对象)注册到二级缓存中。

  • 处理循环依赖:如果bean A依赖于bean B,而bean B又依赖于bean A,当Spring容器创建bean A时,会尝试创建bean B。此时,如果bean B尚未完全创建,Spring容器会从二级缓存中获取bean B的早期引用,并将其注入到bean A中。

  • 使用三级缓存:在bean B的创建过程中,如果检测到对bean A的依赖,Spring容器会从三级缓存中获取bean A的早期引用,并将其注入到bean B中。这样,即使bean A尚未完全创建,bean B也可以继续创建。

  • 初始化Bean:当所有依赖都注入完成后,Spring容器会调用bean的afterPropertiesSet方法进行初始化,并将bean从二级缓存移动到一级缓存中。

  • 完成创建:此时,bean A和bean B都已完成创建,并且相互引用,循环依赖问题得到解决。

需要注意的是,Spring只能解决单例作用域的bean的循环依赖问题,对于原型作用域的bean,Spring无法处理循环依赖。

下面V哥将通过一个简单的示例来解释Spring如何解决循环依赖的问题,并结合一个业务场景进行说明。

假设我们有一个电商系统,其中有两个组件:OrderService(订单服务)和PaymentService(支付服务)。这两个服务相互依赖:OrderService需要使用PaymentService来处理支付,而PaymentService又需要OrderService来确认订单状态。

首先,我们定义这两个服务的接口和实现类。

OrderService.java

java 复制代码
public class OrderService {
    private PaymentService paymentService;

    public void setPaymentService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    public void createOrder() {
        // 创建订单逻辑
        paymentService.processPayment();
    }
}

PaymentService.java

java 复制代码
public class PaymentService {
    private OrderService orderService;

    public void setOrderService(OrderService orderService) {
        this.orderService = orderService;
    }

    public void processPayment() {
        // 处理支付逻辑
        orderService.confirmOrderStatus();
    }
}

在这两个类中,OrderService通过setter方法注入了PaymentService,而PaymentService也通过setter方法注入了OrderService,这就形成了一个循环依赖。

接下来,我们配置Spring容器,将这两个bean定义为单例(singleton)作用域。

applicationContext.xml

java 复制代码
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="orderService" class="com.example.OrderService" scope="singleton"/>
    <bean id="paymentService" class="com.example.PaymentService" scope="singleton"/>
</beans>

现在,当Spring容器启动时,它会创建这两个bean。由于它们之间存在循环依赖,Spring将使用三级缓存来解决这个问题:

  • 创建OrderService Bean:Spring首先尝试创建OrderService bean,并将其注册到二级缓存中。

  • 注入PaymentService:在创建OrderService的过程中,Spring需要注入PaymentService。由于PaymentService尚未完全创建,Spring会尝试从二级缓存中获取PaymentService的早期引用。

  • 创建PaymentService Bean:此时,Spring开始创建PaymentService bean,并将其注册到二级缓存中。

  • 注入OrderService:在创建PaymentService的过程中,Spring需要注入OrderService。由于OrderService已经在二级缓存中,Spring会将其早期引用注入到PaymentService中。

  • 完成初始化:一旦所有依赖都注入完成,Spring会调用每个bean的afterPropertiesSet方法(如果有的话),然后将它们从二级缓存移动到一级缓存中。

  • 解决循环依赖:由于Spring使用了三级缓存来存储bean的早期引用,即使在bean完全创建之前,也可以将它们注入到其他bean中,从而解决了循环依赖问题。

通过这个示例,我们可以看到Spring如何解决循环依赖的问题,并确保了OrderService和PaymentService可以正常工作,即使它们之间存在相互依赖的关系。这种机制使得Spring容器可以灵活地处理复杂的依赖关系,而不需要开发者手动干预。

通过这种方式,Spring框架有效地解决了单例bean的循环依赖问题,确保了依赖注入的顺利进行。关注【威哥爱编程】一起卷起来。

相关推荐
ya888g9 分钟前
GESP C++四级样题卷
java·c++·算法
【D'accumulation】20 分钟前
令牌主动失效机制范例(利用redis)注释分析
java·spring boot·redis·后端
小叶学C++26 分钟前
【C++】类与对象(下)
java·开发语言·c++
2401_8543910829 分钟前
高效开发:SpringBoot网上租赁系统实现细节
java·spring boot·后端
Cikiss38 分钟前
微服务实战——SpringCache 整合 Redis
java·redis·后端·微服务
wxin_VXbishe38 分钟前
springboot合肥师范学院实习实训管理系统-计算机毕业设计源码31290
java·spring boot·python·spring·servlet·django·php
Cikiss39 分钟前
微服务实战——平台属性
java·数据库·后端·微服务
无敌の星仔1 小时前
一个月学会Java 第2天 认识类与对象
java·开发语言
OEC小胖胖1 小时前
Spring Boot + MyBatis 项目中常用注解详解(万字长篇解读)
java·spring boot·后端·spring·mybatis·web
2401_857617621 小时前
SpringBoot校园资料平台:开发与部署指南
java·spring boot·后端