高频面试题:Spring 如何解决循环依赖?

https://www.cnblogs.com/zzq6032010/p/11406405.html

 通过上面的步骤可以看出这三个map的优先级。其中singletonObjects里面存放的是初始化之后的单例对象;earlySingletonObjects中存放的是一个已完成实例化未完成初始化的早期单例对象;而singletonFactories中存放的是ObjectFactory对象,此对象的getObject方法返回值即刚完成实例化还未开始初始化的单例对象。所以先后顺序是,单例对象先存在于singletonFactories中,后存在于earlySingletonObjects中,最后初始化完成后放入singletonObjects中

        可以看到,在构造器执行的时候未完成属性的注入,而在调用方法的时候已经完成了注入。下面就一起看看Spring内部是在何时完成的属性注入,又是如何解决的循环依赖。

至此,Spring循环依赖的总结分析结束,一句话来概括一下:Spring通过将实例化后的对象提前暴露给Spring容器中的singletonFactories,解决了循环依赖的问题

 protected void addSingleton(String beanName, Object singletonObject) {
2         synchronized (this.singletonObjects) {
3             this.singletonObjects.put(beanName, singletonObject);//添加单例对象到map中
4             this.singletonFactories.remove(beanName);//从早期暴露的工厂中移除,此map在解决循环依赖中发挥了关键的作用
5             this.earlySingletonObjects.remove(beanName);//从早期暴露的对象map中移除
6             this.registeredSingletons.add(beanName);//添加到已注册的单例名字集合中
7         }
8     }

 

关于Spring bean的创建,其本质上还是一个对象的创建,既然是对象,读者朋友一定要明白一点就是,一个完整的对象包含两部分:当前对象实例化和对象属性的实例化。

在Spring中,对象的实例化是通过反射实现的,而对象的属性则是在对象实例化之后通过一定的方式设置的。

  • Spring是通过递归的方式获取目标bean及其所依赖的bean的;
  • Spring实例化一个bean的时候,是分两步进行的,首先实例化目标bean,然后为其注入属性。

结合这两点,也就是说,Spring在实例化一个bean的时候,是首先递归的实例化其所依赖的所有bean,直到某个bean没有依赖其他bean,此时就会将该实例返回,然后反递归的将获取到的bean设置为各个上层bean的属性的。

简言之,两个池子:一个成品池子,一个半成品池子。能解决循环依赖的前提是:spring开启了allowCircularReferences,那么一个正在被创建的bean才会被放在半成品池子里。在注入bean,向容器获取bean的时候,优先向成品池子要,要不到,再去向半成品池子要。

出现循环依赖一定是你的业务设计有问题。高层业务和底层业务的划分不够清晰,一般,业务的依赖方向一定是无环的,有环的业务,在后续的维护和拓展一定非常鸡肋

构造注入无法解决循环依赖,而spring的三级缓存的存在是的,setter注入不存在循环依赖
 
的确,工程问题和理论是两码事,工程内大家怎么方便怎么做,spring官方推构造器注入,在国外推广得也很不错,但是一到国内,受大量二流教程和培训班的影响,大家只会怎么方便怎么做,现在80%以上的人还是@Autowired字段的,还有为数不少的在用@Resource。
小蚊子大人
原文地址:https://www.cnblogs.com/ywsheng/p/15001541.html