Spring中循环依赖
来源:互联网 发布:java string 编码 编辑:程序博客网 时间:2024/06/08 18:34
Spring中bean的实例化方法主要是 prototype和singleton两种。其中singleton的循环依赖包括两种:构造器循环依赖,和setter的循环依赖。prototype中的循环依赖。
1.singleton 构造器循环依赖
代码示例:
public class CycleA { private CycleB cycleB; public CycleA(CycleB cycleB) { this.cycleB = cycleB; } public void doSomething() { System.out.println("this is the CycleA ... "); }}public class CycleB { private CycleC cycleC; public CycleB(CycleC cycleC) { this.cycleC = cycleC; } public void doSomething() { System.out.println("this is the CycleB ..."); }}public class CycleC { private CycleA cycleA; public CycleC(CycleA cycleA) { this.cycleA = cycleA; } public void doSomething() { System.out.println("this is the CycleC ..."); }}
配置文件示例:
<bean id="cycleA" class="com.test.bean.blog.myCycleDependencies.bean.CycleA"> <constructor-arg name="cycleB" ref="cycleB"/> </bean> <bean id="cycleB" class="com.test.bean.blog.myCycleDependencies.bean.CycleB"> <constructor-arg name="cycleC" ref="cycleC"/> </bean> <bean id="cycleC" class="com.test.bean.blog.myCycleDependencies.bean.CycleC"> <constructor-arg name="cycleA" ref="cycleA"/> </bean>
这个是一个Singleton构造器循环依赖的实例。构造器的循环依赖无法解决。CycleA依赖于CycleB,CycleB依赖于CycleC 。CycleC依赖于CycleA。在创建的时候,当构造CycleC的时候需要去创建CycleA。这个时候这个时候需要判断出发生了循环。
关键点:一、当创建CycleC的时候,需要创建CycleA,是在哪里被触发的。二、如何发现CycleA是一个循环依赖的。
- 如何发现CycleA是一个循环依赖的
可以看一下代码:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "'beanName' must not be null"); synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); } beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<Exception>(); } try { singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // Has the singleton object implicitly appeared in the meantime -> // if yes, proceed with it since the exception indicates that state. singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } if (newSingleton) { addSingleton(beanName, singletonObject); } } return (singletonObject != NULL_OBJECT ? singletonObject : null); } }
以上代码是获取一个单例对象。首先Object singletonObject = this.singletonObjects.get(beanName);
获取到对应的bean。如果获取失败,就开始调用beforeSingletonCreation(beanName);
开始创建。
代码如下:
protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }}
其中由于CycleA在最开始创建的时候已经被写入到this.singletonsCurrentlyInCreation中了,这个beanName被写入到set中了。在创建过程中这个对象依赖到自身了,返回false。通过一个Set来记录下创建当前bean中依赖的bean。如果发现自己依赖自己就是一个循环依赖。
- 当创建CycleC的时候,需要创建CycleA,是在哪里被触发的。
在通过CycleC的构造函数来创建bean的时候,发现会依赖CycleA的实例,这个时候首先创建CycleA的对象。具体代码如下:
protected BeanWrapper autowireConstructor( String beanName, RootBeanDefinition mbd, Constructor<?>[] ctors, Object[] explicitArgs) { return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs); }
将通过构造器来生成一个BeanWrapper的工作委托给ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs)
。这个过程中需要将构造器中依赖的CycleA先实例化出来。
这个工作委托给BeanDefinitionValueResolver
类中的public Object resolveValueIfNecessary(Object argName, Object value)
方法
看下这个方法的实现由于在配置文件中定义了ref,在Spring内部被转换为RuntimeBeanReference对象:
具体的实现是通过BeeanFactory.getBean()来获取到所依赖的CycleA的实例。
具体如下第351行:
- 总结
以上就是Spring中单例构造器循环依赖的过程。在开始创建CycleA的时候,在准备实力化的时候将CycleA写到singletonsCurrentlyInCreation
中,表示当前的bean对象开始实例化。
然后开始创建的过程中,当将一个CycleA创建为一个BeanWrapper的时候,首先需要CycleA的构造函数中依赖的CycleB的对象,这个时候就开始创建CycleB,这个时候也会重复上面的过程将CycleB写到singletonsCurrentlyInCreation
。
往复以上过程,又一次创建CycleA的时候,发现singletonsCurrentlyInCreation
已经有了一个对象,表示一个对象循环依赖了。抛出异常。
2.prototype 循环依赖
代码示例还是之前的示例,将每个bean的scope改为prototype
类型。
prototype循环依赖的处理过程如下,由于prototype只负责创建一个对象。循环依赖的判定逻辑是在创建CycleA的时候,发现依赖了CycleA,在多线程中,可能会出现这种情况,但是这种并不是循环依赖,所以使用ThreadLocal来隔离开不同的循环依赖。
在创建对象之前先检查是否是循环依赖了:
看下具体实现:
从ThreadLocal中获取到对应beanName是否在创建,如果在创建,就抛出异常。其中,当只有一个beanName的时候使用的String,当有多个beanName的时候存的是Set。
在创建之前将beanName的信息写到ThreadLocal中去:
和上面相同,需要判断是一个还是多个,多个需要将String转变为Set。
在创建完成的额时候需要将beanName从ThreadLocal中删除:
总结
prototype循环依赖主要是使用ThreadLocal来记录当前bean的依赖情况,创建之前先判断一下是否循环依赖,如果没有循环依赖将当前的beanName写到ThreadLocal中去。在创建完成的时候将当前beanName删除。
Singleton的setter循环依赖
相对于基于Singleton构造器循环依赖和prototype中判断当前bean是否正在创建set中,如果在就直接抛出异常。Singleton的setter循环依赖中判断是否是循环依赖也是用了和构造器相同的方法,但是将创建一个对象的过程拆分为两个步骤:initBean
和popuBean
。initBean
:主要负责生成一个Bean的实例,这个实例中的属性可能还没有注入。popuBean
向bean中注入属性,在获取属性的过程中可能会循环的去获取其他的bean。(Spring中对于属性的获取都是委托给BeanDefinitionValueResolver
处理的。)
以 CycleA—> CycleB —> CycleC —> CycleA 。 这样一个循环依赖。这些加载的过程是如下过程:(注:initCycleA表示实例化CycleA;popuCycleA表示填充CycleA)。
以上:Spring在实例化的时候是一个递归的过程。在popuCycleA的时候,这个时候发起了initCycleB,然后开始popuCycleB,又递归的发起了initCycleC,然后popuCycleC,这个当中,由于CycleA已经初始化完成了,已经能够返回CycleA的实例。能够完成popuCycleC。然后递归的完成popuCycleB,然后完成popuCycleA。最终将所有的bean初始化完成。
Spring中在处理单例的时候所有的方法都是委托给DefaultSingletonBeanRegistry
处理的。这个当中有三个关键的方法来帮助解决Spring中的循环依赖。
- public Object getSingleton(String beanName)
这个类是在BeanFactory.getBean最开始的地方调用的。
按照注释,返回的实例是可能是一个完全实例化的bean也是一个为注入属性的bean。在DefaultSingletonBeanRegistry中不同的Bean将会被放在不同的容器中。这个后面再介绍。
- public Object getSingleton(String beanName, ObjectFactory singletonFactory)
在这个方法中ObjectFactory中getObject方法提供了一个创建Bean的方法。
以上在createBeanean()是创建一个Bean的方法。createBean做了两件事,initBean和popuBean。在做这两件事情的中间调用了第三个接口
- protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory)
如上图,这个方法调用的时机在initBean完成以后,在popuBean之前。这样在popuBean的过程中通过第一个方法获取到bean的实例的时候就能够获取到bean,而不用重新创建,触发循环校验的机制了。
Spring中单例的对象从创建到完成之间尽力的形态:
DefaultSingletonBeanRegistry中维护了三个Map来存放Bean Map<String, Object> singletonObjects
//缓存了所有的完全实例化成功的bean Map<String, ObjectFactory<?>> singletonFactories
//缓存了当前所有的ObjectFactory。这个ObejctFactorygetObject()中的方法是一个已经实例化的bean Map<String, Object> earlySingletonObjects
//这个bean中缓存的bean是一个已经实例化完成,但是还没有完全pop的bean。
总结
解决单例setter循环依赖的方式主要是将实例化bean的过程分为initBean 和popuBean过程,在这两个阶段之间调用一下第二个方法将bean注入到DefaultSingletonBeanRegistry中去。这样在循环依赖的时候就能够获取到依赖的对象的引用,而不用在一次去再一次initBean,发生循环依赖。
ObjectFactory这个接口是DefaultSingletonBeanRegsitry提供的一个获取bean的策略,这样能够使得类更加的通用。
- Spring中循环依赖
- spring循环依赖
- spring处理循环依赖
- JAVA,循环依赖,Spring
- spring循环依赖
- Spring 循环依赖
- spring 源代码 循环依赖
- spring循环依赖
- Spring 循环依赖
- Spring循环依赖
- spring 源码-循环依赖
- spring循环依赖问题
- spring循环依赖
- Spring处理循环依赖
- Spring循环依赖
- spring 循环依赖注入
- Spring 循环依赖
- Spring中的循环依赖
- 自制书立
- vim中 E212:无法打开并写入文件 的解决办法
- go client get/post
- MyBatis学习总结(一)——MyBatis快速入门
- stl中map与set
- Spring中循环依赖
- Linux下php安装Redis扩展
- leetcode 376. Wiggle Subsequence 最长摆动序列 + 动态规划DP
- 卷积神经网络的训练
- 谈谈编程的学习方法
- JVM内存模型之运行时常量池
- iOS时间选择器
- 欢迎使用CSDN-markdown编辑器
- LeetCode[659]Split Array into Consecutive Subsequences(Java)