Spring轻松解决循环依赖

作者:这堆干货有点猛 时间:2021-11-07 15:06:11 

Spring 框架是一个流行的Java应用程序框架,它提供了许多强大的功能,如依赖注入和面向切面编程。然而在使用 Spring 框架时,我们可能会遇到循环依赖的问题。

这种情况发生在两个或多个 Bean 之间相互依赖的情况下,其中一个 Bean 依赖于另一个 Bean,而另一个 Bean 又依赖于第一个 Bean。在这种情况下,Spring 框架需要解决循环依赖的问题,否则应用程序可能会出现死锁或其他错误。

本文将探讨 Spring 框架是如何解决循环依赖的问题,以及它是如何工作的。我们将分析 Spring 框架的源代码,并提供一些示例来说明 Spring 框架如何解决循环依赖的问题。

解决循环依赖的原理

在 Spring 框架中,当两个或多个 Bean 之间相互依赖时, Spring 框架会创建一个代理对象,该代理对象负责管理这些 Bean 之间的依赖关系。这个代理对象被称为“early proxy”或“exposed proxy”。

当一个 Bean 需要访问另一个 Bean 时, Spring 框架会通过代理对象来获取该 Bean。这个代理对象负责保证 Bean 的实例化顺序,确保每个 Bean 都只被实例化一次,并且在所有依赖关系被满足之前,不会暴露任何未实例化的 Bean 。

Spring 框架解决循环依赖的过程如下:

  • 当 Spring 框架启动时,它会扫描应用程序中的所有 Bean,并将它们注册到一个 Bean Factory中。

  • 当一个 Bean 被实例化时Spring 框架会检查它是否有任何依赖关系。

  • 如果 Bean 有依赖关系,则 Spring 框架会检查这些依赖关系是否已经被创建。

  • 如果依赖关系已经被创建,则 Spring 框架会将依赖关系注入到 Bean 中,并返回该 Bean 的实例。

  • 如果依赖关系还没有被创建,则 Spring 框架会创建一个代理对象(通过 getEarlyBeanReference 方法),并将该代理对象暴露给该 Bean。

  • 当依赖关系被创建时,Spring 框架会使用代理对象来获取依赖关系,并将依赖关系注入到 Bean 中。

  • 当所有依赖关系都被满足时,Spring 框架会返回该 Bean 的实例。

源码解析

为了更好地理解 Spring 框架如何解决循环依赖的问题,我们将分析 Spring 框架的源代码。下面是一个简单的示例,演示了 Spring 框架如何解决循环依赖的问题。

public class A {
   private B b;
   public A() {}
   public void setB(B b) {
       this.b = b;
   }
}
public class B {
   private A a;
   public B() {}
   public void setA(A a) {
       this.a = a;
   }
}
@Configuration
public class AppConfig {
   @Bean
   public A a() {
       return new A();
   }
   @Bean
   public B b() {
       return new B();
   }
}

在这个示例中,类A依赖于类B,而类B又依赖于类A。因此,这个示例展示了一个循环依赖的情况。

当应用程序启动时,Spring 框架会扫描所有的 Bean ,并将它们注册到一个BeanFactory中。

当 BeanFactory创建 Bean 时,它会检查 Bean 是否有任何依赖关系。

在这个示例中,当 BeanFactory 创建 A 和 B 时,它会检查它们之间的依赖关系。由于 A 依赖于 B,而 B 又依赖于 A,因此存在循环依赖的问题。

为了解决这个问题,Spring 框架会创建一个代理对象,该代理对象负责管理A和B之间的依赖关系。在这个示例中,当 BeanFactory 创建 A 时,它会创建一个代理对象,并将该代理对象暴露给A。当BeanFactory创建B时,它会创建一个代理对象,并将该代理对象暴露给 B。这样,A和B就可以通过代理对象来访问彼此,而不会出现循环依赖的问题。

下面是 Spring 框架解决循环依赖的源码示例:

首先 Spring 框架会从缓存中获取需要的 bean:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
       synchronized (this.singletonObjects) {
           singletonObject = this.earlySingletonObjects.get(beanName);
           if (singletonObject == null && allowEarlyReference) {
               ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
               if (singletonFactory != null) {
                   singletonObject = singletonFactory.getObject();
                   this.earlySingletonObjects.put(beanName, singletonObject);
                   this.singletonFactories.remove(beanName);
               }
           }
       }
   }
   return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

上面代码中,框架分别从 singletonObjects、earlySingletonObjects、singletonFactories 中获取需要的实例,如果获取不到,就就行创建,在创建的过程中如果发现需要处理循环依赖,就会调用下面方法获取代理对象:

private Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
   Object exposedObject = bean;
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
       for (BeanPostProcessor bp : getBeanPostProcessors()) {
           if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
               SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
               exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
               if (exposedObject == null) {
                   return exposedObject;
               }
           }
       }
   }
   return exposedObject;
}

在这个示例中,getEarlyBeanReference() 方法是 Spring 框架用来获取代理对象的方法。该方法首先检查 Bean 是否为合成的 Bean ,然后检查该 Bean 是否有任何实例化后的 Bean 后处理器。如果 Bean 有实例化后的 Bean 后处理器,则 Spring 框架会使用这些 Bean 后处理器来获取代理对象。

Spring轻松解决循环依赖

来源:https://blog.csdn.net/wizard_hu/article/details/130081004

标签:Spring,循环依赖
0
投稿

猜你喜欢

  • 网易Java程序员两轮面试 请问你能答对几个?

    2023-11-29 10:32:08
  • Spring Security前后分离校验token的实现方法

    2023-06-26 17:00:30
  • springboot配置redis过程详解

    2022-10-13 12:41:30
  • C#中实现Json序列化与反序列化的几种方式

    2021-11-06 01:31:07
  • springboot整合微信支付sdk过程解析

    2021-12-30 22:47:29
  • SpringBoot整合之SpringBoot整合MongoDB的详细步骤

    2023-11-25 09:55:37
  • java实现上传文件类型检测过程解析

    2023-11-09 12:04:21
  • springboot v2.0.3版本多数据源配置方法

    2023-07-27 06:37:57
  • Android按钮单击事件的四种常用写法总结

    2023-07-15 09:05:18
  • 利用maven引入第三方jar包以及打包

    2023-11-15 04:23:17
  • Mybatis动态SQL foreach标签用法实例

    2023-12-25 07:42:46
  • 编写Java代码对HDFS进行增删改查操作代码实例

    2023-07-08 11:46:42
  • Java内存区域管理详解

    2023-11-10 23:44:42
  • Java语言实现简单FTP软件 FTP上传下载队列窗口实现(7)

    2021-07-25 04:01:58
  • Toolbar制作菜单条过程详解

    2022-11-29 04:13:59
  • JPA如何设置表名和实体名,表字段与实体字段的对应

    2022-07-14 14:36:27
  • 使用Java反射模拟实现Spring的IoC容器的操作

    2023-06-03 17:37:42
  • Java编程Nashorn实例代码

    2022-07-30 05:28:21
  • 对Java中传值调用的理解分析

    2023-05-03 15:22:14
  • SpringBoot项目部署到腾讯云的实现步骤

    2023-01-01 16:58:55
  • asp之家 软件编程 m.aspxhome.com