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 后处理器来获取代理对象。
来源:https://blog.csdn.net/wizard_hu/article/details/130081004
![](/images/zang.png)
![](/images/jiucuo.png)
猜你喜欢
网易Java程序员两轮面试 请问你能答对几个?
Spring Security前后分离校验token的实现方法
![](https://img.aspxhome.com/file/2023/5/60665_0s.png)
springboot配置redis过程详解
![](https://img.aspxhome.com/file/2023/7/69887_0s.png)
C#中实现Json序列化与反序列化的几种方式
![](https://img.aspxhome.com/file/2023/5/71065_0s.png)
springboot整合微信支付sdk过程解析
![](https://img.aspxhome.com/file/2023/6/59886_0s.jpg)
SpringBoot整合之SpringBoot整合MongoDB的详细步骤
![](https://img.aspxhome.com/file/2023/6/59076_0s.png)
java实现上传文件类型检测过程解析
springboot v2.0.3版本多数据源配置方法
![](https://img.aspxhome.com/file/2023/5/57825_0s.png)
Android按钮单击事件的四种常用写法总结
利用maven引入第三方jar包以及打包
![](https://img.aspxhome.com/file/2023/0/59010_0s.jpg)
Mybatis动态SQL foreach标签用法实例
编写Java代码对HDFS进行增删改查操作代码实例
Java内存区域管理详解
![](https://img.aspxhome.com/file/2023/3/59173_0s.png)
Java语言实现简单FTP软件 FTP上传下载队列窗口实现(7)
![](https://img.aspxhome.com/file/2023/3/63343_0s.png)
Toolbar制作菜单条过程详解
JPA如何设置表名和实体名,表字段与实体字段的对应
使用Java反射模拟实现Spring的IoC容器的操作
![](https://img.aspxhome.com/file/2023/1/78021_0s.png)
Java编程Nashorn实例代码
对Java中传值调用的理解分析
SpringBoot项目部署到腾讯云的实现步骤
![](https://img.aspxhome.com/file/2023/7/70557_0s.png)