循环引用及其解决方案

循环引用

  • 出现的问题
  • 解决方案
  • Spring解决方案(源码实现)
    • 一级缓存(singletonObjects)
    • 二级缓存(earlySingletonObjects)
    • 三级缓存(singletonFactories)
  • 使用三级缓存后的效果
  • 暂时解决不了的问题
  • 面试回答

出现的问题

如果我们在写代码的时候发现如下场景:

@Component
public class A {private B b;@Autowiredpublic A(B b){this.b = b;}
}

@Component
public class B {private A a;@Autowiredpublic B(A a){this.a = a;}
}

启动工程会报以下错误,这个错误很好理解,A构造时需要B,B构造时需要A,这不就死循环了,就像下图一样:
在这里插入图片描述

解决方案

再写一个Bean,这个Bean用来维护A和B之间的关系。

事实上,使用构造器产生的循环依赖是无解的:

A a = new A(b);
B b = new B(a);

我们当然不能在其未构造时而使用其引用,但是我们可以采用如下的解决方案:

A a = new A();
B b = new B();
a.setB(b);
b.setA(a);

将其改为setter注入后:

@Component
public class A{private B b;@Autowiredpublic void setB(B b){this.b = b;}
}
@Component
public class B{private A a;@Autowiredpublic void setA(A a){this.a = a;}
}

早期暴露的Bean就是这个new出来的B的实例,早期暴露的引用就是这个B。

Spring解决方案(源码实现)

提供了一个三级缓存,就是三个Map。

一级缓存(singletonObjects)

这里放的是完整的,完全被实例化后的bean。

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

二级缓存(earlySingletonObjects)

早期暴露的单例的bean。

private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

三级缓存(singletonFactories)

当我们new一个bean的时候,我们还没有给这个bean做属性填充,给这个bean做实例化,我们优先把它暴露出去。

暴露出去的bean可能需要加工一下,而不是仅仅把这个引用给暴露出去。

其中放的是lamada表达式,放了函数式接口ObjectFactory。这个接口提供了一个方法getObject,能获得一个实例。

帮你去判断这个A或者这个B是不是需要代理。如果需要代理,工厂给你提供一个代理的实例和代理的引用。如果不需要代理,工厂给你提供一个原生的对象实例和引用。把它放在二级缓存。

使用三级缓存后的效果

此时此刻,a需要setB(b),需要B的一个早期引用,他会去从哪里找。

去一级缓存找,找一个完全初始化的bean,找不到。

然后去二级缓存找,找一个早期暴露的引用,也找不到。

找不到怎么办,到三级缓存中去找,从bean的对象工厂中获取一个bean。这个bean在当前情况就是一个原始bean,不会做任何的处理。

但是如果你有切面,那个工厂bean会对这个bean做代理。代理会把代理后的bean返回给a,a就会设置到代理后的B,而不是原始B。

这里需要知道代理后的bean和原始的bean的对象和两个引用是不一样的。

暂时解决不了的问题

所以此时此刻循环引用的问题就被解决了。但是循环引用会有一些引用解决不了。在代理的情况是解决不了的。

在new一个A和B后,把a和b放到了早期引用,但是a和b都有代理。这个时候会产生职这样一个问题:创建的a的原生对象会依赖一个b的代理对象,然后在创建b的时候,b的原生对象会依赖于a的代理对象。

此时此刻,a和b它们set进来的都是代理对象,它们两并没有产生循环依赖。就是连注入都没有注入。

不是所有场景的循环依赖都会被解决。

面试回答

通过提前暴露的方式来解决,但是如果这个a和b产生这种代理行为,那么他会产生不一样的结果。

参考资料:循环引用,一个面试官老爱问的八股文


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部