spring解决循环依赖的方案示例

 更新时间:2023年05月30日 11:07:21   作者:一叶飘零晋  
这篇文章主要介绍spring如何解决循环依赖,文中有相关的代码示例给大家参考,对我们的学习或工作有一定的帮助,感兴趣的同学可以借鉴阅读

一、代码

@Component
public class BService {
    @Autowired
    private AService aService;
    public void work(){
        System.out.println("bservice的工作");
    }
}
@Component
public class AService {
    @Autowired
    private  BService bService;
    public void work(){
        System.out.println("aservice的工作");
    }
}

二、AService 的bean创建过程

  • spring调用AService的无参构造方法实例化得到AService类得一个aService对象。
  • spring通过依赖注入填充aService中的bservice属性。(先从单例池去找如果没有就创建BService一直循环下去。。。。。
  • 填充其他属性
  • 其他步骤
  • 加入单例池

所以产生循环依赖。

三、spring三级缓存

Spring三级缓存指的是Spring框架在管理Bean时所维护的三级缓存机制,其作用是提高Bean的创建效率和管理效率。

1、singletonObjects:该缓存中缓存的是完全创建好的单例Bean,即在第二级缓存(factoryBeanInstanceCache)中返回了完整的Bean实例。

2、earlySingletonObjects:该缓存中缓存的是未完全创建好的单例Bean实例(即只完成了实例化和初始化部分),主要为了解决循环依赖问题,即当一个Bean A依赖于Bean B,而Bean B又依赖于Bean A时,通过从earlySingletonObjects缓存中获取到还未完成创建的Bean A实例,并将其注入到Bean B中,使得依赖注入操作可以顺利完成。

3、singletonFactories:该缓存中缓存的是创建Bean的工厂,即BeanFactory的getObject()方法返回的Bean,也即Bean的创建过程。主要针对的是动态代理类型的对象。

四、三级缓存的使用过程

1、获取singletonObjects缓存中的Bean实例,如果存在则直接返回Bean对象,否则继续操作;
2、获取earlySingletonObjects缓存中的Bean实例,如果存在则返回Bean对象,否则继续操作。
3、获取singletonFactories缓存中的Bean实例(即Bean的创建工厂),如果存在则通过工厂方法创建Bean实例并保存到earlySingletonObjects缓存中、从singletonFactories缓存中移除并返回Bean实例,否则继续操作。

当一个Bean被创建完成并添加到singletonObjects缓存中后,其它依赖该Bean的Bean便可以通过getBean()方法直接获取到完整的Bean实例,并完成依赖注入操作。

五、解决循环依赖

注意:
spring的依赖注入方式。分为setter注入和构造器注入。spring可以解决setter类型的构造注入,构造器形式的注入解决不掉。
spring的生命周期可以概括为四个大阶段,实例化,属性赋值,初始化,销毁。

六、如果只有一级缓存能否解决依赖的问题

理论上可以,但是实际操作的时候会有问题,一级缓存和二级缓存的区分点,一个存放的是成品对象,一个存放的是半成品对象,当只有一个map的时候就意味着半成品对象和成品对象放到一起,半成品对象不能够直接暴露给外部使用,因为会有空指针异常,所以如果非要用一个map存储就要添加一个标识,来标注是半成品对象还是成品对象。如果按照这样方式设计代码,会很不优雅,所以可以直接用两个map来解决.不需要一个。

七、如果只有二级级缓存能否解决依赖的问题。

理论上可以,但是前提是在创建对象中不能有代理对象。

八、为什么必须要有三级缓存来解决循环依赖问题?为什么三级缓存可以解决带有代理对象的循环依赖问题

1、同一个容器中能否出现同名的不同对象。

不能

2、如果出现了同名的不同对象,应该怎么办。 比如刚开始创建出原始对象,后续创建出了代理对象。

如果在创建过程中出现了同名的不同对象,那么后面创建的对象会覆盖前面所创建的对象。

3、为什么要使用lambda表达式这样的方式,或者为什么要加入三级缓存呢?
对象的属性的赋值是在 populateBean方法完成的

代理对象的创建是在BeanPostProcessor的后置处理方法里面完成的。

public interface BeanPostProcessor {
	// 注意这个方法名称关键的是before这个单词
	Object postProcessBeforeInitialization(Object bean, String beanName) 
        throws BeansException;
    // 注意这个方法名称关键的是after这个单词
	Object postProcessAfterInitialization(Object bean, String beanName) 
        throws BeansException;
}

populateBean()要比BeanPostProcessor的后置方法先执行也就是说

在进行对象属性赋值的时候,代理对象还没有创建出来,那么属性的赋值只能是原始对象而在后续的步骤中又创建出了代理对象,此时的代理对象会有赋值的过程吗?不会,所以会出现一个错误
this means that said other beans do not use the final version of the bean
就是说赋值是原始对象,而最终留下来的是代理对象,所以导致没有使用最终版本的bean对象。

如何解决?
将代理对象的创建过程提前执行,也就是说在进行对象赋值的时候必须要唯一性的确定出到底是原始对象还是代理对象,这个方法是在getEarlyBeanReference方法里执行的,而getEarlyBeanReference是在populateBean方法的中调用的。

为什么使用lambda表达式

lambda相当于延迟执行,因为此方法次方法并不会在方法的调用的时候立即执行,而是在对象必须要进行属性赋值的那一刻执行,也就是说在对象赋值的的那一刻确定出了最终的bean对象。

总结:使用三级缓存本质上是为了解决aop代理问题。当一个对象需要被代理的时候,在整个整个bean创建过程中,包含两个对象,一个是普通对象,一个是代理生成代理对象,bean默认都是单例的,那么在整个过程中三级缓存在getEarlyBeanReference进行了一个判断。如果不需要代理直接放回普通对象,如果需要代理就用代理对象替换。保证了bean的全局唯一性。所以能够解决aop代理问题。

以上就是spring解决循环依赖的方案示例的详细内容,更多关于spring解决循环依赖的资料请关注脚本之家其它相关文章!

相关文章

  • 简单实现Java web服务器

    简单实现Java web服务器

    这篇文章主要为大家详细介绍了简单实现Java web服务器的详细步骤,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • Java实现规则几何图形的绘制与周长面积计算详解

    Java实现规则几何图形的绘制与周长面积计算详解

    随着计算机的发展,人们对图形的计算要求会越来越高。在各行各业中的计算人员会对图形的计算要有便利的要求,规则几何图形问题求解程序应运而生!本文将用Java编写一个程序,可以实现规则几何图形的绘制与周长面积计算,感兴趣的可以了解一下
    2022-07-07
  • Spring三级缓存解决循环依赖

    Spring三级缓存解决循环依赖

    本文主要介绍了Spring三级缓存解决循环依赖,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • 详解使用spring boot admin监控spring cloud应用程序

    详解使用spring boot admin监控spring cloud应用程序

    这篇文章主要介绍了详解使用spring boot admin监控spring cloud应用程序,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • 基于Java快速实现一个简单版的HashMap详解

    基于Java快速实现一个简单版的HashMap详解

    这篇文章主要为大家详细介绍了如何利用Java简单实现一个底层数据结构为数组 + 链表的HashMap,不考虑链表长度超过8个时变为红黑树的情况,需要的可以参考一下
    2023-02-02
  • java简单实现数组的增删改查方法

    java简单实现数组的增删改查方法

    这篇文章主要介绍了Java数组的增删改查的示例,帮助大家更好的利用Java处理数据,感兴趣的朋友可以了解下,希望能给你带来帮助
    2021-07-07
  • javafx实现五子棋游戏

    javafx实现五子棋游戏

    这篇文章主要为大家详细介绍了javafx实现五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • 线程池之jdk1.8 Executors创建线程池的几种方式

    线程池之jdk1.8 Executors创建线程池的几种方式

    这篇文章主要介绍了线程池之jdk1.8 Executors创建线程池的几种方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • 深入了解Java中Synchronized关键字的实现原理

    深入了解Java中Synchronized关键字的实现原理

    synchronized是JVM的内置锁,基于Monitor机制实现,每一个对象都有一个与之关联的监视器 (Monitor),这个监视器充当了一种互斥锁的角色,本文就详细聊一聊Synchronized关键字的实现原理,需要的朋友可以参考下
    2023-06-06
  • Java多线程实现快速切分文件的程序

    Java多线程实现快速切分文件的程序

    这篇文章主要为大家详细介绍了Java多线程实现快速切分文件的相关资料,感兴趣的小伙伴们可以参考一下
    2016-06-06

最新评论