Spring的循环依赖,到底是怎样的?,如何解决Spring的循环依赖。就几个问题,虽然回答的不是很好,但是还是很幸运的拿到了offer。毕竟很少有因为一两个面试题回答不好而通过面试的。还是要看整体表现。既然问了Spring是如何处理循环依赖的,阿芬就得解释一下Spring是如何处理循环依赖的。循环依赖什么是循环依赖?说到循环依赖,这个其实没有那么复杂。比如很简单的说A引用了B,而此时B也引用了A,那么这种情况其实就出现了循环依赖的问题就解决了。其实循环依赖也可以称为循环引用。两个或多个bean相互抱持,最终形成一个闭环。这就是循环依赖,即循环引用。请注意,这不是函数的循环调用,而是对象之间的相互依赖。除非有终止条件,否则循环调用实际上是一个无限循环。否则就是无限循环。Spring中的循环依赖那么Spring的循环依赖是什么?构造函数的循环依赖和field属性的循环依赖那么Spring是如何解决这两种循环依赖的呢?这很特别。构造函数的循环依赖问题其实是一个无解操作。它只能抛出BeanCurrentlyInCreationException。也就是说,Spring没有办法处理这个构造函数造成的循环依赖。抛出了异常,但是字段属性的循环依赖还是有解决办法的。Spring是如何解决循环依赖的这时候我们就不得不看一下Spring对象初始化的过程了。Spring单例对象的初始化主要分为三步:createBeanInstance实例化populateBean填充属性initializeBean初始化createBeanInstance实例化其实就是调用对象的构造方法实例化对象,populateBean其实是对bean的依赖属性的赋值填充,initializeBean是为了调用Springxml中的init方法。这时候我们看到初始化过程,一般应该能猜到哪一步会发生循环依赖。单从bean初始化的角度来看,循环依赖发生在createBeanInstance的实例化和populateBean填充属性之间,发生的循环依赖也是构造函数的循环依赖字段属性的循环依赖,那么Spring是如何解决的这个单例循环依赖?三级缓存那么这些三级缓存是哪三级缓存呢?他们的意思是什么?singletonFactories:单例对象工厂的缓存,用于存放完全初始化的bean,从缓存中取出的bean可以直接使用withattributes),用于解决循环依赖singletonObjects:单例对象缓存,存放bean工厂对象,用于解决循环依赖privatefinalMapsingletonObjects=newConcurrentHashMap<>(256);//一级缓存privatefinalMapearlySingletonObjects=newHashMap<>(16);//二级缓存privatefinalMap>singletonFactories=newHashMap<>(16);//三级缓存如果要分析三级缓存的循环依赖是如何解决的,那么就要知道Spring中对象创建的过程了。对象的创建过程大致可以分为五个步骤,1.protectedTdoGetBean(finalStringname,@NullablefinalClassrequiredType,@NullablefinalObject[]args,booleantypeCheckOnly)doGetBeaninAbstractBeanFactory()方法2.protectedObjectgetSingleton(StringbeanName,booleanallowEarlyReference)DefaultSingletonBeanRegistry中的getSingleton()方法在该方法中,首先从一级缓存中获取singletonObjects。(获取到就直接返回)如果获取不到,对象正在创建,就从二级缓存earlySingletonObjects中获取。如果还是获取不到,让singletonFactories通过getObject()获取,从三级缓存中获取singletonFactory.getObject()(三级缓存)相当于ctrl+x,切三级缓存中的数据到二级缓存。源码如下:protectedObjectgetSingleton(StringbeanName,booleanallowEarlyReference){ObjectsingletonObject=this.singletonObjects.get(beanName);如果(singletonObject==null&&isSingletonCurrentlyInCreation(beanName)){同步(this.singletonObjects){singletonObject=this.earlySingletonObjects.get(beanName);if(singletonObject==null&&allowEarlyReference){ObjectFactory>singletonFactory=this.singletonFactories.get(beanName);if(singletonFactory!=null){singletonObject=singletonFactory.getObject();this.earlySingletonObjects.put(beanName,singletonObject);this.singletonFactories.remove(beanName);}}}}返回单例对象;}3.protectedObjectcreateBean(StringbeanName,RootBeanDefinitionmbd,@NullableObject[]args)AbstractAutowireCapableBeanFactory中的doCreateBean()方法//添加到三级封装if(earlySingletonExposure){if(logger.isTraceEnabled()){logger.trace("急切地缓存bean'"+beanName+"'以允许解析潜在的循环引用");}addSingletonFactory(beanName,()->getEarlyBeanReference(beanName,mbd,bean));}4.protectedvoidpopulateBean(StringbeanName,RootBeanDefinitionmbd,@NullableBeanWrapperbw)AbstractAutowireCapableBeanFactory中的populateBean()方法进行属性赋值有些fans不会贴那么多。大家想必很容易找到源码,里面也有具体方法的注解。Spring解决循环依赖的诀窍就在于singletonFactories的三级缓存。这个缓存的类型是ObjectFactory。这里是解决循环依赖的关键,它发生在createBeanInstance之后,也就是说此时已经创建了单例对象(调用构造函数)。这个对象已经产生了,虽然还不完美(初始化的第二步和第三步还没有进行),但是已经被识别了(可以根据对象引用定位到堆中的对象),所以Spring在这一次,提前曝光了这个对象,让大家知道并使用它。如果你在面试的时候能这样回答,那你至少把这个问题回答好了。但如果你问这个问题,面试官打算继续深挖。既然知道使用三级缓存已经解决了循环依赖的问题,那么还需要使用三级缓存来解决吗?二级缓存不能解决吗?这就给大家带来了另外一个问题,二级缓存能解决吗?其实二级缓存也是可以实现的。如果要自己实现,就得重写AbstractAutowireCapableBeanFactory的doCreateBean方法,//加入三级缓存'"+beanName+"'允许解析潜在的循环引用");}addSingletonFactory(beanName,()->getEarlyBeanReference(beanName,mbd,bean));//从三级缓存中取出并立即放入二级缓存中getSingleton(beanName,true);}如果要使用二级缓存来解决循环依赖,意味着Bean在构造之后会创建一个代理对象,这就违背了Spring的设计原则。Spring结合AOP和Bean的生命周期是在Bean创建完成后通过后处理器AnnotationAwareAspectJAutoProxyCreator来完成的。在这个后处理的postProcessAfterInitialization方法中,AOP代理完成了对初始化的Bean。如果有循环依赖,没办法只能先为Bean创建代理,但如果没有循环依赖,设计是让Bean在生命周期的最后一步完成代理,而不是完成实例化后立即代理。那么,你知道为什么不直接使用二级缓存来处理,而是加一个三级缓存来处理这种循环依赖了吧!