大家好,欢迎来到Tlog4J课堂,我是Jensen。面试官:Spring是如何解决循环依赖问题的?考生:Spring使用三级缓存来解决这个问题面试官:你能解释一下它是如何工作的吗?考生:啊,这个...spring循环依赖其实是spring中一个很典型的问题,也是一个非常难的问题,因为回答这个问题本身就会很绕,而且这不仅仅是一个简单的问题,它会引发一系列的问题来自面试官的一系列致命问题。今天,我们就一起来梳理一下循环依赖涉及到的问题。SpringCircularDependency致命连环问题:什么是循环依赖?循环依赖是指多个对象之间形成闭环的依赖关系。我们都知道,如果在代码中使用两个或多个Bean相互持有引用,就会出现循环依赖,从而导致注入的死循环。这是造成Spring循环依赖的原因之一。.第二个问题:Spring循环依赖有哪些形式?第一种是相互依赖,即A依赖B,B依赖A,它们之间形成循环依赖:第二种是三者之间的依赖,即A依赖B,B依赖C,并且C依赖A,形成循环依赖:第三种是自依赖,即A与A之间形成的循环依赖。现实中,由于依赖程度深、关系复杂等因素,循环依赖不一定一目了然。三问:Spring设计的三级缓存中存储了哪些内容?三级缓存说白了就是三个Map容器??,在框架中随处可见:第一层:一个完全初始化的单例bean。/**单例对象的缓存:bean名称到bean实例。*/privatefinalMapsingletonObjects=newConcurrentHashMap<>(256);第二层:提前暴露的单例对象缓存。/**早期单例对象的缓存:bean名称到bean实例。*/privatefinalMapearlySingletonObjects=newHashMap<>(16);第三层:ObjectFactory工厂bean缓存,存放实例化的beanFactory。/**单例工厂的缓存:bean名称到ObjectFactory。*/privatefinalMap>singletonFactories=newHashMap<>(16);四问:Spring的三级缓存具体工作原理是什么?循环依赖主要是解决这个“死循环”没有“退出”的问题。只要找到这个“出口”,循环依赖就可以轻松解决。下面分四步说一下:当我们通过getBean(A.class)获取A对象实例时,Spring会先去一级缓存中查找。如果发现一级缓存没有找到BeanA,就会创建这个A.Instance,加入到三级缓存中,暴露Bean。此时通过@Autowired依赖注入BeanB时,即getBean(B.class)时,Spring发现在一级缓存中找不到B,于是B重复A的第一步创建实例B.添加到L3缓存并公开bean。当B通过@Autowired依赖注入BeanA时,发现三级缓存已经找到了A的实例,此时如果A实现了AOP,会先创建一个动态代理对象,然后从三级缓存中移动A一级缓存进入二级缓存。至此B依赖注入成功,初始化工作完成,然后Spring将B从三级缓存移至一级缓存,B完成整个初始化工作。最后在A的创建周期中加入Spring,完成A对B的依赖注入,然后继续A的初始化,然后将A从二级缓存移到一级缓存。这里可以做一个总结:如果还不明白,可以多看几遍这个动画:一句话总结:让底层对象先完成初始化,通过三级缓存提前暴露创建的Bean以及二级缓存,让其他Bean率先完成初始化。五问:Spring在什么情况下无法解决循环依赖问题?有四种情况无法解决:多实例bean是通过setter注入的。Bean是通过构造函数注入的。单例的代理bean是通过Setter注入的。设置带@DependsOn注解的Bean。写在最后其实很多同学在回答这个问题的时候,也知道Spring是用三级缓存来处理这个问题的,但是这个问题回答的难点在于很多同学对这个问题不理解,所以当你想在短时间内把这个问题解释清楚给面试官时,你会觉得有点不清楚。以上都是从底层原理升华出来的具体问题。通常各大互联网公司的面试都是这个套路,包括JVM、NIO、MQ等很多基础知识。从框架的工作原理开始,考察自己对框架原理的理解,最后找一些实际问题来解决测试你对这个技术栈的全面掌握,能否解决实际问题。OK,回答好这个问题取决于你对Spring框架的理解是否深入。相信大家再总结一下就会有所收获。
