高频面试问题: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/father-of-little-pig/p/15035056.html
Spring循环依赖的场景有两种:
构造器的循环依赖
field 属性的循环依赖
对于构造器的循环依赖,Spring 是无法解决,只能抛出 BeanCurrentlyInCreationException 异常;对于field 属性的循环依赖,Spring 只解决 scope 为 singleton 的循环依赖,对于scope 为 prototype 的 bean Spring 无法解决,直接抛出 BeanCurrentlyInCreationException 异常。下面重点分析属性依赖的情况。
小蚊子大人