Spring的循环依赖问题一直是Java中高级面试的高频题之一。考点是对Spring中的bean加载过程是否有一定的了解。那么我们来谈谈以下几点。Spring常用的注入方式有哪些?Constructorinjectionsetmethodinjectionannotationinjection关于这方面的知识后面会详细讲,今天的重点不在这些。什么是循环依赖?循环依赖,注意这里说的是依赖,不是调用。这是两个概念,不要混淆。循环依赖从字面上看就是A依赖B,然后B又依赖A。当然这个依赖过程也可以更长一些。两个人不一定需要互相依赖。十个或八个也是循环依赖。只要一个就形成闭环。如图所示,这形成了一个闭环。简单的说,如果一直这样依赖下去,就会死循环。如何解决循环依赖?Spring中有两种循环依赖,构造函数循环依赖和setter循环依赖。构造函数循环依赖当使用构造函数注入方式时,Spring无法解析循环依赖,出错时会报错,并抛出BeanCurrentlyInCreationException。如果模拟一下场景,大致是这样的:创建X1时,发现构造函数需要一个X2类型的对象,所以只能创建X2;创建X2时,发现构造函数需要一个X3类型的对象,所以只能创建X3;创建X3时,再次发现构造函数需要X1对象;它一圈又一圈,最后抛出异常。setter循环依赖主要讲setter循环依赖,通过Spring创建bean时的一级、二级、三级缓存的概念来解决。注意:这里解决的只是单例模式下的setter循环依赖。在非单例模式下还是没有办法解决。这种情况在商业环境中应该尽量避免。创建bean的缓存概念:一级缓存:singletonObjects,可以称为成品池,存放的是已经完全实例化并赋予属性的bean,可以直接使用。二级缓存:earlySingletonObjects,可以称为半成品池,存放的是早期bean的引用以及还没有属性组装的bean。三级缓存:singletonFactories,可以称为工厂池,存放实例化的Bean工厂。通过setter注入时,会经历以下流程。看图说话!1)创建X1时,首先根据构造函数创建一个bean,暴露一个Factory给三级缓存(工厂池),放入二级缓存(半成品池);然后进行属性组装,发现有依赖,查询三级缓存是否存在,不存在则去创建。2)创建X2时,像X1一样去创建X3。3)创建X3时,此时X1已经存在于三级缓存中,所以可以直接注入,然后将X3的bean对象放入一级缓存(成品池)中。4)然后可以依次创建X2和X1,放入一级缓存。5)这样就解决了setter循环依赖问题,核心是三级缓存。注意:这里的bean对象是创建的,当放入一级缓存时,相应的二级和三级缓存会被清空。本文参与SegmentFault的随笔《如何“反杀”面试官?如果您正在阅读,欢迎您加入。
