什么是循环依赖
spring的循环依赖就是A类依赖B类,B类又依赖A类,在创建bean的过程中,在A的属性赋值过程中,需要把B的属性赋值,但是因为B也是一个bean,所以在给B赋值之前先把B初始化,但是在B初始化之前先进行属性赋值,需要把A的属性赋值,再去创建A,这样下去就会形成一个环;
spring是如何解决循环依赖的呢?
spring引入了缓存,bean的创建过程大致分为三步:实例化,属性赋值,初始化
如果只是引入一级缓存的话,就需要在实例化之后把这个实例化的bean放到缓存中,这样就导致缓存中存的是不完整的bean,因为实例化的bean只是一个空壳,这样肯定是不行的,因此就需要引入二级缓存,这样的话一级缓存就当到初始化之后存放完整的bean,二级缓存放到实例化之后,存放不完整的bean,避免了循环依赖,就算是aop创建代理对象,用二级缓存也就足够了,这是在不考虑代码规范以及单一原则的情况下可以用二级缓存就可以了,但是spring的代码是高度可扩展的,不允许代码耦合度太高,所以引入三级缓存,三级缓存主要是存放函数式接口的,也就是在实例化后可以存放到三级缓存中,在循环依赖再次调用getsigleton方法的时候执行回调函数,并放入二级缓存中,等跳出循环依赖会再次调用getsigleton方法从二级缓存中取出回调函数执行结果存放到一级缓存中
例如A依赖B,B依赖A
首先在创建A的时候,属性赋值的时候发现依赖B,会调用getBean(“B”),发现三级缓存中没有,就去创建B
在创建B的时候,属性赋值的时候发现依赖A,调用getBean(“A”),去三级缓存中找,发现可以找到,会执行回调函数,并把A的回调函数存放到二级缓存中,并返回给B赋值A的属性,并把B放入一级缓存中,再次回到A的属性赋值,把B的属性赋值后,在A的初始化后再次调用getsigleton方法从二级缓存中拿到A的真正的初始化数据(因为在第一次实例化A的时候放入三级缓存的数据是原实例,在循环依赖出口调用回调函数有可能是原实例的动态代理,所以在把A放入一级缓存之前需要从二级缓存中再次取一次值放入一级缓存中)
一级缓存:存的是完整的bean,
二级缓存:为了和完整的bean做分离
三级缓存:解决循环依赖,并对getBean方法进行解耦,存的是函数接口,调用bean的后置处理器
spring没有解决构造函数的循环依赖,因为构造函数还没有进行实例化,没有进行实例化就没有办法往缓存中放,就没有循环依赖的出口
spring也没有解决多例下的循环依赖,因为多例bean是不会存到缓存中的
spring的循环依赖
最新推荐文章于 2024-10-09 05:30:00 发布