本文共 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 MapsingletonObjects = 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/