解决循环依赖看看spring是怎么做的
循环依赖的定义:
假设有两个对象A,B;对象A依赖B,对象B依赖A。
@Component
public class A {
private B b;
public void setB(B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
public void setA(A a) {
this.a = a;
}
}
spring中如何解决循环依赖:
通过递归,递归的终止条件是能在map中找到被所需要的bean。
分析:
首先理解创建一个对象分两步
首先new出来一个对象,然后给对象里的变量赋值。
解析:
这里我们以上面的首先初始化A对象实例为例进行讲解。
首先Spring尝试通过ApplicationContext.getBean()方法获取A对象的实例,由于Spring容器中还没有A对象实例,因而其会创建一个A对象
然后发现其依赖了B对象,因而会尝试递归的通过ApplicationContext.getBean()方法获取B对象的实例
但是Spring容器中此时也没有B对象的实例,因而其还是会先创建一个B对象的实例。
读者需要注意这个时间点,此时A对象和B对象都已经创建了,并且保存在Spring容器中了,只不过A对象的属性b和B对象的属性a都还没有设置进去。
在前面Spring创建B对象之后,Spring发现B对象依赖了属性A,因而还是会尝试递归的调用ApplicationContext.getBean()方法获取A对象的实例
因为Spring中已经有一个A对象的实例,虽然只是半成品(其属性b还未初始化),但其也还是目标bean,因而会将该A对象的实例返回。
此时,B对象的属性a就设置进去了,然后还是ApplicationContext.getBean()方法递归的返回,也就是将B对象的实例返回,此时就会将该实例设置到A对象的属性b中。
这个时候,注意A对象的属性b和B对象的属性a都已经设置了目标对象的实例了
读者朋友可能会比较疑惑的是,前面在为对象B设置属性a的时候,这个A类型属性还是个半成品。但是需要注意的是,这个A是一个引用,其本质上还是最开始就实例化的A对象。
而在上面这个递归过程的最后,Spring将获取到的B对象实例设置到了A对象的属性b中了
这里的A对象其实和前面设置到实例B中的半成品A对象是同一个对象,其引用地址是同一个,这里为A对象的b属性设置了值,其实也就是为那个半成品的a属性设置了值。
☎️https://www.cnblogs.com/asker009/p/14376955.html
- 三级缓存解决了循环依赖的问题
一级缓存:存放完整的bean
二级缓存:存放加载bean的没有被实例化的对象,是不完整的bean
三级缓存:存放代理的bean,第三级缓存里实际存入的是ObjectFactory接口签名的回调实现,把原始的包返回。
那么你知道三级缓存吗?三级缓存是解决什么问题的?
- 三级缓存是解决循环依赖问题的,一级缓存存放的是完整的Bean,二级缓存存放的是半成品的Bean,三级缓存存放的是ObjectFactory。在创建Bean时会把半成品的Bean包装成为ObjectFactory放入到三级缓存中,在进行该Bean的注入时就需要从缓存中拿到半成品的对象,如果没有三级缓存的存在,那么因为注入的Bean有可能需要完成动态代理,但是你注入的Bean并没有完成动态代理,就导致了最终注入的Bean是个半成品。二级缓存实际上是可以不需要存在的,每次都直接从三级缓存中拿其实也是可以的,只不过三级缓存中有一堆for循环,每次就导致了性能的降低,因此在完成动态代理之后会将对象从三级缓存中移除,并且加入到二级缓存中,下次就只需要从二级缓存中拿就能拿到完整的对象了。
上一篇: 春季食欲不好怎么办 六款养生粥帮你开胃
下一篇: 夏秋之交多喝九款汤 防暑去燥滋阴养肺