博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring循环依赖问题分析和解决
阅读量:2065 次
发布时间:2019-04-29

本文共 3499 字,大约阅读时间需要 11 分钟。

项目路径: 下的spring-10

循环依赖出现的三种情况
1、构造器参数循环依赖
2、setter方式单例,singleton
3、setter方式原型模式,prototype
一、构造参数依赖,新建StuServiceA和StuServiceB,之间相互引用

public class StuServiceA {    private StuServiceB stuServiceB;    public StuServiceA(StuServiceB stuServiceB) {        this.stuServiceB = stuServiceB;    }    public StuServiceA() {    }    public StuServiceB getStuServiceB() {        return stuServiceB;    }    public void setStuServiceB(StuServiceB stuServiceB) {        this.stuServiceB = stuServiceB;    }}
public class StuServiceB {    private StuServiceA stuServiceA;    public StuServiceA getStuServiceA() {        return stuServiceA;    }    public void setStuServiceA(StuServiceA stuServiceA) {        this.stuServiceA = stuServiceA;    }    public StuServiceB(StuServiceA stuServiceA) {        this.stuServiceA = stuServiceA;    }    public StuServiceB() {    }}

xml配置

运行测试结果:

请求的bean当前正在创建中:是否存在无法解析的循环引用?

Spring容器会将每一个正在创建的Bean标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中。如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉。

上述源码分析:

Spring容器先创建单例stuServiceA,stuServiceA依赖stuServiceB,然后将A放在“当前创建Bean池”中,此时创建stuServiceB, stuServiceB依赖stuServiceA, 但是,此时stuServiceA已经在池中,所以会报错,因为在池中的Bean都是未初始化完的,所以会依赖错误 ,(初始化完的Bean会从池中移除)
 

二、Setter方式、单例

xml配置

此时需要对bean的声明周期过程要熟悉,

可以看出,先实例化之后,才设置的对象属性
上图分析:
Spring先是用构造实例化Bean对象 (默认无参构造器),此时Spring会将这个实例化结束的对象放到一个Map中,并且Spring提供了获取这个未设置属性的实例化对象引用的方法。
当Spring实例化了stuServiceA、stuServiceB后,紧接着会去设置对象的属性,此时stuServiceA依赖stuServiceB,就会去Map中取出存在里面的单例StudentB对象,以此类推,不会出来循环的问题。

三、Setter方式、原型
xml配置

运行结果,报循环依赖的问题

scope="prototype" 意思是 每次请求都会创建一个实例对象。

两者的区别是:有状态的bean都使用Prototype作用域,无状态的一般都使用singleton单例作用域。
对于“prototype”作用域Bean,Spring容器无法完成依赖注入,因为“prototype”作用域的Bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean。
所以上述三种情况可以看出只有Setter单例模式的可以成功

 

Spring解决单例模式循环依赖原理(三级缓存)

Spring循环依赖的理论依据其实是Java基于引用传递,当我们获取到对象的引用时,对象的field或者或属性是可以延后设置的。
三级缓存所在的类名:DefaultSingletonBeanRegistry

//单例对象的cache    private final Map
singletonObjects = new ConcurrentHashMap(256);//单例对象工厂的cacheprivate final Map
> singletonFactories = new HashMap(16);//提前曝光的单例对象的cacheprivate final Map
earlySingletonObjects = new HashMap(16);

getSingleton方法

@Nullable    protected Object getSingleton(String beanName, boolean allowEarlyReference) {        Object singletonObject = this.singletonObjects.get(beanName);        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {            Map var4 = this.singletonObjects;            synchronized(this.singletonObjects) {                singletonObject = this.earlySingletonObjects.get(beanName);                if (singletonObject == null && allowEarlyReference) {                    ObjectFactory
singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }

可以看出当获取bean时会先从singletonObjects对象中获取,如果没有则判断提前曝光的单例对象中earlySingletonObjects 时候有(正在创建的bean池中),如果也没有则从单例对象工厂singletonFactories中获取。如果能获取到则移除对应的singletonFactory,将singletonObject放入到earlySingletonObjects,其实就是将三级缓存提升到二级缓存中!

 

 

转载地址:http://bswmf.baihongyu.com/

你可能感兴趣的文章
高并发、高可用、高可靠微服务架构7大顶级设计思维模型
查看>>
如何使用 registry 存储的特性
查看>>
凉了,stress 无论如何也无法打满 CPU
查看>>
除了 k8s,留给 k 和 s 中间的数字不多了!
查看>>
使用 wrk 压测并精细控制并发请求量
查看>>
Ceph 故障排查笔记 | 万字经验总结
查看>>
使用 Go 从零开始实现 CNI 可还行?
查看>>
KubeSphere 3.1.0 GA:混合多云走向边缘,让应用无处不在
查看>>
Containerd 1.5 发布:重磅支持 docker-compose!
查看>>
基于 Kubernetes 的 Spring Could 微服务 CI/CD 实践
查看>>
5.15 相约上海!2021 年度首届云原生 Meetup | KubeSphere & Friends
查看>>
使用 Cilium 作为网络插件部署 K8s + KubeSphere
查看>>
万变不离其宗,性能优化也有章可循
查看>>
别再乱用 Prometheus 联邦了,分享一个 Prometheus 高可用新方案
查看>>
Litmus 实践:让群魔在混沌中乱舞,看 K8s 能撑到何时
查看>>
Prometheus 存储层的演进
查看>>
Kubernetes 边缘节点抓不到监控指标?试试这个方法!
查看>>
不对全文内容进行索引的 Loki 到底优秀在哪里
查看>>
太强大了!这些 Go 的难点问题都能解决!
查看>>
万字长文:K8s 创建 pod 时,背后到底发生了什么?
查看>>