Spring 循环依赖
循环依赖
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图:
这里不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,最终会导致内存溢出错误,除非有终结条件。
依赖配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!--<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">--> <!--<property name="locations">--> <!--<list>--> <!--<value>classpath*:/resous.properties</value>--> <!--</list>--> <!--</property>--> <!--</bean>--> <bean id="schoolA" class="com.fyp.spring.source.beans.SchoolA"> <property name="name" value="张三"></property> <property name="daynatic" value="test"></property> <property name="students" ref="studentB"></property> </bean> <bean id="studentB" class="com.fyp.spring.source.beans.StudentsB"> <property name="name" value="张三"></property> <property name="age" value="30"></property> <property name="school" ref="schoolA"></property> </bean> </beans>
** * @ClassName: School * @description: * @author * @date: 2021/1/25 21:48 */ @Data public class SchoolA { /** * */ private StudentsB students; /** * */ private String name; /** * */ private String daynatic; }
/** * @ClassName: Students * @description: * @author * @date: 2021/1/11 11:35 */ @Data public class StudentsB { /** * */ private String name; /** * */ private Integer age; private SchoolA school; }
1.new Bean过程
finishBeanFactoryInitialization ->beanFactory.preInstantiateSingletons(); getBean()-doGetBean- @Override public Object getBean(String name) throws BeansException { //doGetBean才是真正向IoC容器获取被管理Bean的过程 return doGetBean(name, null, null, false); } //AbstractBeanFactory protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; //这里返回null Object sharedInstance = getSingleton(beanName); //忽略部分代码 if (mbd.isSingleton()) { //这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象 //从 getSingleton 获取,获取不到 通过匿名 内部类创建 /** * getSingleton new 对象-> 存放三级缓存->填充属性-> 初始化 这个流程走完之后放入一级缓存 */ sharedInstance = getSingleton(beanName, () -> { try { ////第一次 创建bean 入口 创建一个指定Bean实例对象,如果有父级继承,则合并子类和父类的定义 return createBean(beanName, mbd, args); } } //getSingleton try { //调用传过来的create singletonObject = singletonFactory.getObject(); newSingleton = true; } public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { try { //调用 AbstractBeanFactory.createBean //getObject()调用的是匿名方法,从前面传过来的createBean()方法 singletonObject = singletonFactory.getObject(); newSingleton = true; } //createBean->doCreateBean try { beanInstance = this.doCreateBean(beanName, mbdToUse, args); } //doCreateBean 创建对象 if (instanceWrapper == null) { instanceWrapper = this.createBeanInstance(beanName, mbd, args); } return autowireNecessary ? this.autowireConstructor(beanName, mbd, (Constructor[])null, (Object[])null) : this.instantiateBean(beanName, mbd); //instantiateBean ctor.newInstance(args) //存入三级缓存 this.addSingletonFactory(beanName, () -> { return this.getEarlyBeanReference(beanName, mbd, bean); }); //createBean 方法调用结束后 存放如一级缓存 //getSingleton if (newSingleton) { addSingleton(beanName, singletonObject); }
1.1反射创建对象
1.2schoolA创建(只是new了一个对象,在堆上面开辟了一块空间)成功存放入三级缓存
SchoolA只是只是new了一个空的对象,对象里面的属性值都是null
此时对象A依赖属性为null
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); Map var3 = this.singletonObjects; synchronized(this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { //存放如三级缓存 this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
2.填充属性 populateBean(StudentB)
this.populateBean(beanName, mbd, instanceWrapper) //设置属性值 this.applyPropertyValues(beanName, mbd, bw, (PropertyValues)pvs); public Object resolveValueIfNecessary(Object argName, @Nullable Object value) return this.resolveReference(argName, ref); //属性值为bean /** * 循环依赖对象 这里调用 AbstractBeanFactory.getBean * getBean ->doGetBean * * Object sharedInstance = getSingleton(beanName); */ bean = this.beanFactory.getBean(refName); @Override public Object getBean(String name) throws BeansException { //doGetBean才是真正向IoC容器获取被管理Bean的过程 return doGetBean(name, null, null, false); } //真正实现向IOC容器获取Bean的功能,也是触发依赖注入功能的地方 protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { //根据指定的名称获取被管理Bean的名称,剥离指定名称中对容器的相关依赖 //如果指定的是别名,将别名转换为规范的Bean名称 final String beanName = transformedBeanName(name); Object bean; // Eagerly check singleton cache for manually registered singletons. //先从缓存中取是否已经有被创建过的单态类型的Bean //对于单例模式的Bean整个IOC容器中只创建一次,不需要重复创建 //1 第一次获取对象为 null // 第二次获取 从三级缓存里面获取对象 Object sharedInstance = getSingleton(beanName); ..... } //从三级缓存中获取 @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; }
this.populateBean(beanName, mbd, instanceWrapper) //设置属性值 this.applyPropertyValues(beanName, mbd, bw, (PropertyValues)pvs); public Object resolveValueIfNecessary(Object argName, @Nullable Object value) return this.resolveReference(argName, ref); //属性值为bean bean = this.beanFactory.getBean(refName); //从三级缓存中获取 @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; }
对象填充完成
2.1循环依赖
A对象获取, 给对象A填充属性的时候发现依赖B对象,则对B对象进行 new->放入三级缓存->populateBean,发现B对象依赖A对象,在执行A对象的getBean,doGetBean
doGetBean的过程会先进行Object sharedInstance = this.getSingleton(beanName); 此时会getSingleton从缓存中获取,从三级缓存中获取到对象A,对象B进行 init之后完成对象B的创建存放如一级缓存
applyPropertyValues-> Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); @Nullable public Object resolveValueIfNecessary(Object argName, @Nullable Object value) { if (value instanceof RuntimeBeanReference) { RuntimeBeanReference ref = (RuntimeBeanReference)value; return this.resolveReference(argName, ref); } @Nullable private Object resolveReference(Object argName, RuntimeBeanReference ref) { bean = this.beanFactory.getBean(refName); this.beanFactory.registerDependentBean(refName, this.beanName); } //doGetBean从三级缓存获取为空,再次调用 执行new bean 存放如三级缓存 if (mbd.isSingleton()) { sharedInstance = this.getSingleton(beanName, () -> { try { return this.createBean(beanName, mbd, args); } catch (BeansException var5) { this.destroySingleton(beanName); throw var5; } });
依赖属性注入
实际set方法
依赖对象注入
2.1获取依赖属性studentB
2.2执行studentB的获取
2.3 创建studentB
studentB对象为空
执行获取bean ,传入一个createBean
调用传入的createBean方法
doCreateBean创建studentB
创建一个StudentB对象,属性值都为null
2.4StudentB创建成功加入三级缓存
此时三级缓存里面存放两个非完整的对象
2.5StudentB对象进行属性填充
2.6在填充SchoolB的属性的时候发现依赖属性SchoolA
2.7 从三级缓存里面获取SchoolA
因为A在创建的时候放入三级缓存的value是一个工厂,此时从工厂获取Bean
singletonFactory.getObject()方法调用的是getEarlyBeanReference,这里没有BeanPostProcessors 实现类所以还是返回的原始对象而非代理对象,如果这里有Aop的话
话创建一个代理对象返回
2.8将SchoolA从三级对象中获取,并且存入二级缓存,然后从三级缓存中移除StudentA
此时已经获取到B对象依赖的对象A了,也就是B对象的populateBean执行完成了,继续执行对象A的初始化工作
2.9对象StudentB完成创建对象
2.10对象StudentB创建完成(完整的对象), 存放入一级缓存
对象B对象完成之后存放如一级缓存
3对象B创建完成之后,继续对象SchoolA进行后续初始化操作
3.1对象A存入三级缓存
此时对象A和对象B有完整的对象存放如一级缓存
A对象完成populateBean之后,A对象创建完成存放如一级缓存
//bean 经过 new , populateBean ,initializeBean 之后bean创建完成 //放入到一级缓存中,同时移除二三级缓存 protected void addSingleton(String beanName, Object singletonObject) { Map var3 = this.singletonObjects; synchronized(this.singletonObjects) { this.singletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }
4.返回创建成功的Bean
5.三级缓存容器
//一级缓存,存放完整的bean实例
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
//三级缓存,存放非完整对象,对象new完之后,没有初始化,非完整bean,解决循环依赖,根据需要判断是否需要创建代理对象如果需要则创建,否则直接返回非完整对象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
//二级缓存,对象创建完,非初始化的对象,二级缓存主要存放代理对象,保证代理对象循环引用 private final Map<String, Object> earlySingletonObjects = new HashMap(16);
6.流程图
7.面试题
1.为什么Spring不能解决构造器的循环依赖?
从流程图应该不难看出来,在Bean调用构造器实例化之前,一二三级缓存并没有Bean的任何相关信息,在实例化之后才放入三级缓存中,因此当getBean的时候缓存并没有命中,这样就抛出了循环依赖的异常了。加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。
- 首先创建A,缓存没有,将自己添加到正在创建的集合,然后构造的时候发现自己依赖B,因此转向获取B,关键在这里,此时A还未来得及将自己放到缓存
- 然后获取B,也是一样,在构造的那一步发现自己依赖A,因此去获取A
- 到这应该清楚了,缓存中获取不到A,因此需要走创建流程,而创建流程会将自己添加到正在创建的集合里面去,而A在之前已经添加过一次,这一次就会报错,如code1所示,下面的添加会返回false,因此会抛出BeanCurrentlyInCreationException异常
-
//singletonsCurrentlyInCreation是一个set集合,重复添加会返回false !this.singletonsCurrentlyInCreation.add(beanName)
2.为什么多实例Bean不能解决循环依赖?
多实例Bean是每次创建都会调用doGetBean方法,根本没有使用一二三级缓存,肯定不能解决循环依赖。
- 非单例的每次创建都是new一个对象,IOC不会对其进行缓存,如果AB循环依赖,那么创建A1的时候依赖B1,然后要创建B1的时候发现依赖A,由此再去创建A2,然后A2有需要一个B2,一直下去没有尽头。不过Spring在代码中是有判断的,在AbstractBeanFactory的doGetBean中,如果缓存中获取Bean失败,就会判断需要获取的Bean是否为处于创建中的原型Bean,如果是就会抛出异常
-
if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }
3.Spring中如何解决循环依赖?
通过三级缓存解决的
4.为什么要有用三级缓存?
三级缓存的value是一个ObjectFactory<?>,泛型,第一是为了扩展,在需要的时候通过具体的实现得到具体的对象,早起引用对象这个动作给用于预留一个扩展接口
二级缓存主要是存放Object,如果在A类上配置AOP,是否需要生成一个代理对象?
在生成代理对象之前A对象已经生成成功了,需要三级缓存的主要意义在于,你所需要的类有可能是简单对象(实例化,初始化),也可能是需要进行代理的对象,当向三级缓存中放置匿名内部类的时候,可以在获取的时候根据是否简单对象,还是代理对象创建具体的对象,按需创建对象
不管在什么时候需要对象,都是在需要的时候通过匿名内部类来生成,而不是提前放置一个好的对象
- 一个对象先在三级缓存里面存放factory
- 获取的时候通过匿名内部类获取真正的对象(代理或者Object),通过该方法getEarlyBeanReference如果用了代理就创建代理对象,没用就返回Object
5.如果只保留二级缓存会怎样?
- (白话讲解就是:如果需要一个代理对象那么每次都需要创建一个新的代理对象)换个角度假如只有2级缓存,那么用户扩展的动作就要放到二级缓存来做,这样每一次来获取的时候如果一级缓存没有都需要在二级缓存执行一次singletonFactory.getObject()触发一下获取Bean,这样这个方法很可能会被执行多次,每次都会创建一个新的对象,由此肯定会引起问题,引入了三级缓存singletonFactory.getObject()只会在第一次获取的时候,前两级缓存都不存在才会触发一次,并立刻将自己放到二级缓存,并清空三级缓存,后面每一次获取的都是这次创建的这个对象。
- 如果不需要扩展,放进去的早期引用就是不完整的Bean,不考虑给其他后置处理器扩展,这样使用2级缓存是可以的。A直接将自己放入二级缓存,循环依赖的时候B直接从二级缓存获取A的早期引用保证B初始化成功再将自己加到一级缓存,然后A初始化成功后添加到一级缓存,但是这样就不能扩展了,因为这个扩展点很重要,在AOP的AnnotationAwareAspectJAutoProxyCreator就通过这个扩展点来保证代理对象的返回和代理对象的循环依赖问题解决(保证循环依赖的时候,返回的对象也是代理对象)
- 二级缓存主要作用是存放代理对象,创建一次代理对象,后续需要的时候直接获取无需再次创建
6.Spring需要依赖的解决一定要通过三级缓存么 ?
是的,一级缓存能不能解决问题,不能,如果只有一级缓存,那么意味着完整状态和非完整状态的对象都存在,如果此时获取一个对象,恰巧获取的是非完整对象(里面的属性都为空 ),刚好要用里面的属性,但是属性都为空,直接都报空指针了,那怎么办?如何解决的?可以添加二级缓存
7.二级缓存能不能解决问题?为什么非要使用三级缓存?
7.为什么要将三级缓存remove并且放入到二级缓存?
问题1:解决性能问题:三级缓存的value是一个创建Bean的工厂,工厂创建bean肯定是耗时的,如果当前的Bean被很多地方循环依赖了,那么无需多次通过工厂创建对象,如果当前对象已经被工厂创建过了,就从三级级缓存移除放入二级缓存,下次再有该bean的获取,就可以直接从二级缓存里面获取了,符合源码中的从二级缓存获取。
protected Object getSingleton(String beanName, boolean allowEarlyReference) { //从一级缓存获取 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { //从二级缓存获取 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { //调用三级缓存 调用的是匿名内部类 // () -> getEarlyBeanReference(beanName, mbd, bean) singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
问题2:其实三级缓存的最终目的的都是保存同一个对象,所以三个缓存里面不可能都存放一样的对象,既然已经存放到二级缓存了,三级缓存也没有必要了,从map里面remove是为了GC。