在上一篇Spring源码剖析中,咱们跳过了一部分对于Spring解决循环依赖局部的代码,为了填上这个坑,我这里另开一文来好好探讨下这个问题。
首先解释下什么是循环依赖,其实很简略,就是有两个类它们相互都依赖了对方,如下所示:
@Componentpublic class AService { @Autowired private BService bService;}
@Componentpublic class BService { @Autowired private AService aService;}
AService和BService显然两者都在外部依赖了对方,单拎进去看好像看到了多线程中常见的死锁代码,但很显然Spring解决了这个问题,不然咱们也不可能失常的应用它了。
所谓创立Bean实际上就是调用getBean() 办法,这个办法能够在AbstractBeanFactory这个类外面找到,这个办法一开始会调用getSingleton()办法。
// Eagerly check singleton cache for manually registered singletons.Object sharedInstance = getSingleton(beanName);
这个办法的实现长得很有意思,有着一堆if语句。
protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { synchronized(this.singletonObjects) { singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); // 从三级缓存里取出放到二级缓存中 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } } } return singletonObject;}
但这一坨if很好了解,就是一层层的去获取这个bean,首先从singletonObjects中获取,这外面寄存的是曾经齐全创立好的单例Bean;如果取不到,那么就往下走,去earlySingletonObjects外面取,这个是晚期曝光的对象;如果还是没有,那么再去第三级缓存singletonFactories外面获取,它是提前裸露的对象工厂,这里会从三级缓存里取出后放到二级缓存中。那么总的来说,Spring去获取一个bean的时候,其实并不是间接就从容器外面取,而是先从缓存里找,而且缓存一共有三级。那么从这个办法返回的并不一定是咱们须要的bean,前面会调用getObjectForBeanInstance()办法去失去实例化后的bean,这里就不多说了。
但如果缓存外面确实是取不到bean呢?那么阐明这个bean确实还未创立,须要去创立一个bean,这样咱们就会去到前一篇生命周期中的创立bean的办法了。回顾下流程:实例化--属性注入--初始化--销毁。那么咱们回到文章结尾的例子,有ServiceA和ServiceB两个类。一般来说,Spring是依照天然程序去创立bean,那么第一个要创立的是ServiceA。显然一开始缓存里是没有的,咱们会来到创立bean的办法。首先进行实例化阶段,咱们会来到第一个跟解决循环依赖无关的代码,在实例化阶段的代码中就能够找到。
// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}
首先看看第一行,earlySingletonExposure这个变量它会是什么值?
它是有一个条件表达式返回的,一个个来看,首先,mbd.isSingleton()。咱们晓得Spring默认的Bean的作用域都是单例的,因而这里失常来说都是返回true没问题。第二个,this.allowCircularReference,这个变量是标记是否容许循环援用,默认也是true。第三个,调用了一个办法,isSingletonCurrentlyInCreation(beanName),进入该代码能够看出它是返回以后的bean是不是失常创立,显然也是true。因而这个earlySingletonExposure返回的就是true。
接下来就进入了if语句的实现外面了,也就是addSingletonFactory()这个办法。看到外面的代码中呈现singletonFactories这个变量是不是很相熟?翻到下面的getSingleton()就晓得了,其实就是三级缓存,所以这个办法的作用是通过三级缓存提前裸露一个工厂对象。
/** * Add the given singleton factory for building the specified singleton * if necessary. * <p>To be called for eager registration of singletons, e.g. to be able to * resolve circular references. * @param beanName the name of the bean * @param singletonFactory the factory for the singleton object */protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }}
接下来,回顾下上一章节说的实例化之后的步骤,就是属性注入了。这就意味着ServiceA须要将ServiceB注入进去,那么显然又要调用getBean()办法去获取ServiceB。ServiceB还没有创立,则也会进入这个createBean()办法,同样也会来到这一步依赖注入。ServiceB中依赖了ServiceA,则会调用getBean()去获取ServiceA。此时的获取ServiceA可就不是再创立Bean了,而是从缓存中获取。这个缓存就是下面getSingleton()这个办法外面咱们看到的singletonFactory。那么这个singletonFactory哪里来的,就是这个addSingletonFactory()办法的第二个参数,即getEarlyBeanReference()办法。
/** * Obtain a reference for early access to the specified bean, * typically for the purpose of resolving a circular reference. * @param beanName the name of the bean (for error handling purposes) * @param mbd the merged bean definition for the bean * @param bean the raw bean instance * @return the object to expose as bean reference */protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); } } return exposedObject;}
查看bp.getEarlyBeanReference(exposedObject, beanName)的实现,发现有两个,一个是spring-beans下的SmartInstantiationAwareBeanPostProcessor,一个是spring-aop下的AbstractAutoProxyCreator。咱们在未应用AOP的状况下,取的还是第一种实现。
default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { return bean;}
那么令人诧异的是,这办法间接返回了bean,也就是说如果不思考AOP的话,这个办法啥都没干,就是把实例化创立的对象间接返回了。如果思考AOP的话调用的是另一个实现:
public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey);}
能够看出,如果应用了AOP的话,这个办法返回的实际上是bean的代理,并不是它自身。那么通过这部分咱们能够认为,在没有应用AOP的状况下,三级缓存是没有什么用的,所谓三级缓存实际上只是跟Spring的AOP无关的。
好了咱们当初是处于创立B的过程,但因为B依赖A,所以调用了获取A的办法,则A从三级缓存进入了二级缓存,失去了A的代理对象。当然咱们不须要放心注入B的是A的代理对象会带来什么问题,因为生成代理类的外部都是持有一个指标类的援用,当调用代理对象的办法的时候,实际上是会调用指标对象的办法的,所以所以代理对象是没影响的。当然这里也反馈了咱们实际上从容器中要获取的对象实际上是代理对象而不是其自身。
那么咱们再回到创立A的逻辑往下走,能看到前面实际上又调用了一次getSingleton()办法。传入的allowEarlyReference为false。
if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } ... }}
翻看下面的getSingleton()代码能够看出,allowEarlyReference为false就相当于禁用三级缓存,代码只会执行到通过二级缓存get。
singletonObject = this.earlySingletonObjects.get(beanName);
因为在后面咱们在创立往B中注入A的时候曾经从三级缓存取出来放到二级缓存中了,所以这里A能够通过二级缓存去取。再往下就是生命周期前面的代码了,就不再持续了。
那么当初就会有个疑难,咱们为什么非要三级缓存,间接用二级缓存仿佛就足够了?
看看下面getEarlyBeanReference()这个办法所在的类,它是SpringAOP主动代理的要害类,它实现了SmartInstantiationAwareBeanPostProcessor,也就是说它也是个后置处理器BeanPostProcessor,它有着自定义的初始化后的办法。
/** * Create a proxy with the configured interceptors if the bean is * identified as one to proxy by the subclass. * @see #getAdvicesAndAdvisorsForBean */@Overridepublic Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean;}
很显著它这里是earlyProxyReferences缓存中找不到以后的bean的话就会去创立代理。也就是说SpringAOP心愿在Bean初始化后进行创立代理。如果咱们只应用二级缓存,也就是在这个中央
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
间接调用getEarlyBeanReference()并将失去的晚期援用放入二级缓存。这就意味着无论bean之间是否存在相互依赖,只有创立bean走到这一步都得去创立代理对象了。然而Spring并不想这么做,不信本人能够入手debug一下,如果ServiceA和ServiceB之间没有依赖关系的话,getEarlyBeanReference()这个办法压根就不会执行。总的来说就是,如果不应用三级缓存间接应用二级缓存的话,会导致所有的Bean在实例化后就要实现AOP代理,这是没有必要的。
最初咱们从新梳理下流程,记得Spring创立Bean的时候是依照天然程序的,所以A在前B在后:
咱们首先进行A的创立,但因为依赖了B,所以开始创立B,同样的,对B进行属性注入的时候会要用到A,那么就会通过getBean()去获取A,A在实例化阶段会提前将对象放入三级缓存中,如果没有应用AOP,那么实质上就是这个bean自身,否则是AOP代理后的代理对象。三级缓存singletonFactories会将其寄存进去。那么通过getBean()办法获取A的时候,外围其实在于getSingleton()办法, 它会将其从三级缓存中取出,而后放到二级缓存中去。而最终B创立完结回到A初始化的时候,会再次调用一次getSingleton()办法,此时入参的allowEarlyReference为false,因而是去二级缓存中取,失去真正须要的bean或代理对象,最初A创立完结,流程完结。
所以Spring解决循环依赖的原理大抵就讲完了,但根据上述的论断,咱们能够思考一个问题,什么状况的循环依赖是无奈解决的?
依据下面的流程图,咱们晓得,要解决循环依赖首先一个大前提是bean必须是单例的,基于这个前提咱们才值得持续探讨这个问题。而后根据上述总结,能够晓得,每个bean都是要进行实例化的,也就是要执行结构器。所以能不能解决循环依赖问题其实跟依赖注入的形式无关。
依赖注入的形式有setter注入,结构器注入和Field形式。
Filed形式就是咱们平时用的最多的,属性上加个@Autowired或者@Resource之类的注解,这个对解决循环依赖无影响;
如果A和B都是通过setter注入,显然对于执行结构器没有影响,所以不影响解决循环依赖;
如果A和B相互通过结构器注入,那么执行结构器的时候也就是实例化的时候,A在本人还没放入缓存的时候就去创立B了,那么B也是拿不到A的,因而会出错;
如果A中注入B的形式为setter,B中注入A为结构器,因为A先实例化,执行结构器,并创立缓存,都没有问题,持续属性注入,依赖了B而后走创立B的流程,获取A也能够从缓存外面能取到,流程一路通顺。
如果A中注入B的形式为结构器,B中注入A为setter,那么这个时候A先进入实例化办法,发现须要B,那么就会去创立B,而A还没放入三级缓存里,B再创立的时候去获取A就会获取失败。
好了,以上就是对于Spring解决循环依赖问题的所有内容,这个问题的答案我是很久之前就晓得了,但真的只是晓得答案,这次是本人看源码加debug一点点看才晓得为啥是这个答案,尽管还做不到彻底学的通透,但确实能对这个问题的了解的更为粗浅一点,再接再厉吧。