【JAVA】Spring解决循环依赖

  1. 什么是循环依赖?
    在创建A的时候发现A中的属性需要B对象,那就先去创建B对象,又发现B中的属性需要A对象,那又去创建A,形成死循环,这就是循环依赖。

  2. 什么是单例池,什么是一级缓存?
    一级缓存:singletonObjects,存放初始化后的单例对象

  3. 什么是二级缓存,它的作用是什么?
    二级缓存:earlySingletonObjects,存放实例化,未完成初始化的单例对象(未完成属性注入的对象)

  4. 什么是三级缓存,它的作用是什么?
    三级缓存:singletonFactories,存放ObjectFactory对象

  5. 为什么Spring一定要用三级缓存来解决循环依赖?
    https://www.cnblogs.com/semi-sub/p/13548479.html
    https://www.jianshu.com/p/2f08c524f12e

  6. 三级缓存解决循环依赖的底层源码分析
    假设A依赖B,B依赖A,Spring创建A实例的过程如下:
    A依次执行doGetBean方法、依次查询三个缓存是否存在该bean、没有就createBean,实例化完成(早期引用,未完成属性装配),放入三级缓存中,接着执行populateBean方法装配属性,但是发现装配的属性是B对象,走下面步骤。
    创建B实例,依次执行doGetBean、查询三个缓存、createBean创建实例,接着执行populateBean发现属性中需要A对象。
    再次调用doGetBean创建A实例,查询三个缓存,在三级缓存singletonFactories得到了A的早期引用(在第一步的时候创建出来了),将它放到二级缓存并移除3级缓存并返回,B完成属性装配,一个完整的对象放到一级缓存singletonObjects中。
    B完成之后就回到A了,A得到完整的B,肯定也完成全部初始化,也存入一级缓存中。
    解决了循环依赖问题。这里可能很多初学者很蒙,什么是早期引用,其实学过C语言的指针的话就比较好理解了,这里的引用就是地址,所以先开辟内存而不封装属性,我后面再给它封装属性,那引用得到的永远是这个地址的最新值,所以就可以先给引用地址后面再封装属性值,这个一定要与传值区分开来,单纯的传值和传地址是不一样的

  7. Spring AOP的底层原理分析

  8. 有哪些情况下的循环依赖是Spring解决不了的?
    如果是构造方法去注入,那循环依赖问题没有方法解决,如果是Set方法注入,那就可以使用三级缓存来解决,Spring中就是这样的解决的。

  9. 为什么@Lazy注解可以用来解决循环依赖?

10.为什么不使用二级缓存?
如果仅仅是解决循环依赖问题,二级缓存也可以,但是如果注入的对象实现了AOP,那么注入到其他bean的时候,不是最终的代理对象,而是原始的。通过三级缓存的ObjectFactory才能实现类最终的代理对象。

11.Spring在创建Bean的过程
实例化,对应方法:AbstractAutowireCapableBeanFactory中的createBeanInstance方法
属性注入,对应方法:AbstractAutowireCapableBeanFactory的populateBean方法
初始化,对应方法:AbstractAutowireCapableBeanFactory的initializeBean

实例化,简单理解就是new了一个对象
属性注入,为实例化中new出来的对象填充属性
初始化,执行aware接口中的方法,初始化方法,完成AOP代理

singletonObjects,一级缓存,存储的是所有创建好了的单例Bean
earlySingletonObjects,完成实例化,但是还未进行属性注入及初始化的对象
singletonFactories,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象

你可能感兴趣的:(【JAVA】Spring解决循环依赖)