spring循环依赖相关面试题

  1. 请你解释下spring中的三级缓存
  2. 三级缓存分别是什么?三个map有什么异同?
  • singletonObjects(一级缓存)
    存放已经经历了完整生命周期的Bean对象
  • earlySingletonObjects(二级缓存)
    存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完整)
  • singletonFactories(三级缓存)
    存放生成Bean的工厂
  1. 什么是循环依赖, 请你谈谈?看过spring源码吗?一般我们说的spring容器是什么?
  2. 如何检测存在循环依赖?实际开发中见过循环依赖的异常吗?
  3. 多例的情况下, 循环依赖问题为什么无法解决?

spring循环以来的3个Map和4大方法

// 3个Map
singletonObjects, singletonFactories, earlySingletonObjects
// 4大方法
getSingleton, doCreateBean, populateBean, addSingleton
// 注意这里
Map> singletonFactories = new HashMap(16);

第一层singletonObjects存放的是已经初始化好的bean
第二层earlySingletonObjects存放的是实例化了的,但是未初始化的bean
第三层singletonFactories存放的是FactoryBean, 加入A类实现了FactoryBean,那么依赖注入的时候不是A类,而是A类产生的bean

总体的大致流程

  1. A创建过程中需要B,于是把自己放到三级缓存里,去实例化B
  2. B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A。然后把A放到了二级缓存里面,并删除三级缓存里的A
  3. B顺利初始化完毕,把自己放到一级缓存里面(此时B里面的A依旧是创建中的状态),然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建。并将A自己放到一级缓存里面

spring循环依赖的断点

  1. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    public ClassPathXmlApplicationContext(
            String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }

其中断点打在ClassPathXmlApplicationContext的refresh()这一行

  1. 断点打在org.springframework.context.support.AbstractApplicationContext#refresh中的finishBeanFactoryInitialization(beanFactory);这个方法
  2. org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
    断点打在beanFactory.preInstantiateSingletons();这一行
  3. org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons断点打在getBean(beanName);这一行
  4. org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String) -> org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
    也就是说从getBean到doGetBean
  5. org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean断点打在Object sharedInstance = getSingleton(beanName);这一行
  6. org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean断点打在这行
sharedInstance = getSingleton(beanName, () -> {
    try {
        return createBean(beanName, mbd, args);
    }
    catch (BeansException ex) {
        // Explicitly remove instance from singleton cache: It might have been put there
        // eagerly by the creation process, to allow for circular reference resolution.
        // Also remove any beans that received a temporary reference to the bean.
        destroySingleton(beanName);
        throw ex;
    }
});
  1. 断点打在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory)singletonObject = singletonFactory.getObject();这一行
  2. 断点打在org.springframework.beans.factory.support.AbstractBeanFactory#doGetBeanreturn createBean(beanName, mbd, args);这一行
  3. 断点打在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])Object beanInstance = doCreateBean(beanName, mbdToUse, args);这一行
    至此,到了第二个重要方法doCreateBean()
  4. 断点打在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBeanaddSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));这一行。
    注意,这一行表示要将A放到三级缓存里面了
  5. 断点打在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactorythis.singletonFactories.put(beanName, singletonFactory);这一行。
    这一行表示,将A和生产它的bean工厂放到三级缓存里
  6. 断点打在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBeanpopulateBean(beanName, mbd, instanceWrapper);这一行。
    表示开始填充A的属性。至此,到了第三个重要方法populateBean()
  7. 进入上面的populateBean方法,断点打在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBeanapplyPropertyValues(beanName, mbd, bw, pvs);这一行
  8. 进入上面的applyPropertyValues方法,断点打在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyPropertyValuesObject resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);这一行
  9. 进入上面的resolveValueIfNecessary方法,断点打在return resolveReference(argName, ref);这行
  10. 进入上面的resolveReference方法,断点打在bean = this.beanFactory.getBean(resolvedName);这一行。
    至此回到了第6步中的doGetBean方法
  11. 接下来的步骤与上面类似,在创建B的过程中我们发现需要属性A, 断点来到org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveReferencebean = this.beanFactory.getBean(resolvedName);这一行,开始寻找属性A
  12. 断点打在org.springframework.beans.factory.support.AbstractBeanFactory#doGetBeanObject sharedInstance = getSingleton(beanName);这一行,进入该方法
  13. 断点打在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)ObjectFactory singletonFactory = this.singletonFactories.get(beanName);这一行。在这里,从三级缓存中找到了A,于是将A放到二级缓存中,并从三级缓存中移除
  14. 在上一步之后,B中的A属性填充完毕了, 返回到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBeanpopulateBean(beanName, mbd, instanceWrapper);的下一行exposedObject = initializeBean(beanName, exposedObject, mbd);,断点打在这里。
  15. 断点打在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory)addSingleton(beanName, singletonObject);这一行,这也是上面说的4大方法中的一个
  16. 进入这个方法,这个方法将b加入到一级缓存中,并在二级缓存和三级缓存中移除
  17. 返回到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBeanpopulateBean(beanName, mbd, instanceWrapper);的下一行exposedObject = initializeBean(beanName, exposedObject, mbd);,断点打在这里。这里,A中的b属性填充完毕了
  18. 断点打在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory)addSingleton(beanName, singletonObject);这一行。进入这个方法,在这里,将a放到一级缓存,并将它从二级和三级缓存中删除

来张图片看一下整体流程吧



参考资料

尚硅谷Java大厂面试题全集(java面试,周阳主讲)-Java面试大厂高频面试题阳哥
p160

你可能感兴趣的:(spring循环依赖相关面试题)