当前位置: 首页 > 后端技术 > Java

spring是如何解决循环依赖的

时间:2023-04-01 19:56:46 Java

在Spring中,我们使用getBean方法从容器中获取一个bean,然后在AbstractApplicationContext类中的getBean方法getBeanpublicObjectgetBean(Stringname)throwsBeansException{assertBeanFactoryActive();返回getBeanFactory().getBean(名称);}然后在AbstractBeanFactory类中输入getBeanpublicObjectgetBean(Stringname)throwsBeansException{returndoGetBean(name,null,null,false);}然后进入doGetBean方法,比较长,截取它的核心点,先说getSingleton方法//从缓存中获取指定的bean对象sharedInstance=getSingleton(beanName);进入DefaultSingletonBeanRegistry的getSingleton方法publicObjectgetSingleton(StringbeanName){returngetSingleton(beanName,true);}protectedObjectgetSingleton(StringbeanName,booleanallowEarlyReference){//先从一级缓存中找到ObjectsingletonObject=this.singletonObjects.get(beanName);//如果没有一级缓存,当前bean在创建过程中if(singletonObject==null&&isSingletonCurrentlyInCreation(beanName)){synchronized(this.singletonObjects){//从二级缓存中查找singletonObject=this.earlySingletonObjects.get(beanName);//如果没有二级缓存,允许暴露早期引用if(singletonObject==null&&allowEarlyReference){//从三级缓存中查找bean工厂ObjectFactory得到(豆名);if(singletonFactory!=null){//调用getObject方法生成beansingletonObject=singletonFactory.getObject();//放入二级缓存this.earlySingletonObjects.put(beanName,singletonObject);//从三级缓存中移除this.singletonFactories.remove(beanName);}}}}返回单例对象;看到这里,Spring在解析循环依赖时也使用了缓存。缓存声明和定义如下:privatefinalMapsingletonObjects=newConcurrentHashMap<>(256);privatefinalMapearlySingletonObjects=newHashMap<>(16);privatefinalMap>singletonFactories=newHashMap<>(16);singletonObjects,一级缓存存放最终产物,即完成实例化(只调用构造函数)、属性注入、初始化beanearlySingletonObjects,二级缓存存放的是半成品,或者早期暴露的对象,即只有实例完成的BeanSingletonFactories,未完成的属性注入和初始化,三级缓存,存放的是可以拿到半成品的工厂.在前几节中,我们自己解决了循环依赖。一级缓存的存在是为了解决单线程情况下的循环依赖,二级缓存的存在是为了兼容多线程,提高获取bean的效率三级缓存存在的意义是什么?解决循环依赖,核心前提之一就是bean必须是单例,那么我们来看doGetBean方法中的第二个核心代码,即处理单例bean/处理单例bean的代码if(mbd.isSingleton()){//sharedInstance是从缓存中获取的bean,一般来说,这里是一个nullsharedInstance=getSingleton(beanName,()->{try{returncreateBean(beanName,mbd,args);}catch(BeansExceptionex){destroySingleton(beanName);抛出ex;}});bean=getObjectForBeanInstance(sharedInstance,name,beanName,mbd);}getSingleton方法中的第二个参数是一个函数式接口类型的ObjectFactory,所以这里可以直接使用lambda表达式传入一个默认实现createBean方法。进入getSingleton(beanName,objectFactory)方法,简化代码如下:publicObjectgetSingleton(StringbeanName,ObjectFactorysingletonFactory){//如果不在缓存中,会使用getObject方法创建synchronized(this.singletonObjects){ObjectsingletonObject=this.singletonObjects.get(beanName);if(singletonObject==null){//标记当前bean正在创建,如果多次创建,这里会抛出异常beforeSingletonCreation(beanName);布尔newSingleton=false;//执行getObject,即执行外部传递的createBean方法singletonObject=singletonFactory.getObject();newSingleton=true;//省略异常处理,当异常发生时,newSingleton=false//取消bean正在创建的标志afterSingletonCreation(beanName);if(newSingleton){//管理缓存,去掉三级缓存和二级缓存,加入一级缓存addSingleton(beanName,singletonObject);}}返回singletonObje克拉;}}到了getObject方法,就会进入AbstractAutowireCapableBeanFactorycreateBean方法。核心方法就是这句话:ObjectbeanInstance=doCreateBean(beanName,mbdToUse,args);代码:protectedObjectdoCreateBean(finalStringbeanName,finalRootBeanDefinitionmbd,final@NullableObject[]args)throwsBeanCreationException{//实例化beanBeanWrapperinstanceWrapper=createBeanInstance(beanName,mbd,args);booleanearlySingletonExposureing=()&&this.allowCircularReferences&&isSingletonCurrentlyInCreation(beanName));if(earlySingletonExposure){//加入L3缓存,getEarlyBeanReference会返回单例工厂addSingletonFactory(beanName,()->getEarlyBeanReference(beanName,mbd,bean));}对象exposedObject=bean;//属性注入populateBean(beanName,mbd,instanceWrapper);//初始化exposedObject=initializeBean(be名称、暴露对象、mbd);if(earlySingletonExposure){//从二级缓存中查找ObjectearlySingletonReference=getSingleton(beanName,false);if(earlySingletonReference!=null){//返回二级缓存中的bean,这里可能是代理对象exposedObject=earlySingletonReference;}}返回exposedObject;}看看getEarlyBeanReference返回的是一个什么样的单例工厂(但其实是一个工厂,而是一个获取早暴露对象的逻辑)//从容器中寻找实现了InstantiationAwareBeanPostProcessor接口的后处理器/如果后处理器实现了SmartInstantiationAwareBeanPostProcessor接口if(bpinstanceofSmartInstantiationAwareBeanPostProcessor){SmartInstantiationAwareBeanPostProcessoribp=(SmartInstantiationAwareBeanPostProcessor)bp;//调用SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法exposedObject=ibp.getEarlyBeanReference(exposedObject,beanName);如果当前没有实现InstantiationAwareBeanPostProcessor接口的后处理器,即当前bean没有被任何AOP拦截后,直接返回传入的bean。至此,源码已经分析的差不多了。拿A和B的循环依赖,画出整个过程。从上面的分析可以看出,解决循环依赖的本质就是使用缓存。对于多实例下的循环依赖,每次注入一个新的对象,根本不使用缓存,所以无法解决多实例下的循环依赖。自然排序后,如果一开始就注入了构造函数,则无法使用缓存。set注入之所以可以利用缓存,是因为实例化和属性赋值可以分开,为缓存利用留有余地。三级缓存的理解是在单线程的情况下,即如果getBean只支持串行操作,那么一级缓存其实就够用了。二级缓存将半成品和成品对象分开,这样在多线程的情况下,不会获取到不完整的对象实例。而且同时支持多线程查询缓存,一定程度上提高了性能。三级缓存存放单例对象工厂。准确的说是函数式接口的实现,也就是一段生成对象的逻辑。如果对象被代理,工厂的getObject返回代理后的对象,否则返回原始对象。这里可能有人会有疑问,为什么我们需要三级缓存呢?实例化阶段之后,直接把原始对象或者代理对象放到二级缓存里不就好了吗?理论上是的。一般来说,在bean的生命周期中,代理是在初始化完成之后创建的。而如果代理出现在循环依赖中,则不能等到初始化完成,否则注入到B中的A是原始对象,而不是代理对象。所以对A的代理需要在B的属性注入之前的那一刻完成,这个阶段也一定发生在A的初始化之前。此时A实例化之后,如果有特定的后处理器,也就是说有了代理,那么直接把A的代理对象放入二级缓存,不管以后会不会有循环依赖。事实上,在这期间,无法检测到最后是否会出现循环依赖,更无法预测未来。如果存在循环依赖,可以从二级缓存中获取A的代理对象,注入到B中。如果没有循环依赖,A的代理对象就没有用,做着无用功。这时候加一层缓存呢?A实例化后,三级缓存中只存放生成对象的逻辑。生成代理对象还是生成原始对象是在循环依赖发生时决定的。当发生循环依赖时,比如A需要注入B时,会调用L3缓存中的工厂逻辑生成A的代理对象注入B。当没有循环依赖时,代理对象A的仍然是初始化完成后创建的,这与Bean生命周期中的处理是一致的。所以三级缓存的存在是出于代理的考虑。一方面可以避免在没有循环依赖的情况下直接将代理对象存放在二级缓存中的无用功,另一方面也可以最大限度的统一Bean的生命周期,可谓是最好的两个世界,一箭双雕。

最新推荐
猜你喜欢