spring源码关于循环引用的笔记,spring解决循环依赖原理
00-1010前言循环依赖实现过程
目录
在上一篇文章中,我们分析了spring中Bean的实例化过程,但是没有分析循环依赖。在本文中,我们将看看Spring是如何解决循环依赖的。在之前讲spring的过程中,我们提到了spring的一个singleton pool,singletonObjects,用来存储创建的bean。我们也提到过,这张地图也可以说是狭义的弹簧容器。
private final MapString,objectsingletonobjects=new concurrent hashmapstring,Object(256);其实spring在缓存bean的过程中并不只有这个Map。让我们看一下类DefaultSingletonBeanRegistry。里面其实有三张地图,也就是常说的spring L3缓存。
singletonObjects的缓存: bean name-bean instance */private final MapString,Object singleton objects=new concurrent hashmapstring,Object(256);/**早期单例对象的缓存: bean name-bean instance */private final MapString,Object earlySingletonObjects=new HashMapString,Object(16);/**单例工厂缓存: bean名称-ObjectFactory */private final MapString,object factory?singleton factories=new hashmap string,ObjectFactory?(16);从上到下,有一级到三级缓存。在这里,我们对三级缓存有了一个初步的了解,后面再详细分析。
00-1010我们开始分析spring循环依赖的注入实现过程。首先编写两个bean,并将彼此注入其中:
@ component public class service a { @ auto wired service b service b;public service b getServiceB(){ system . out . println( get ServiceB );退货服务b;} } @ component public class service b { @ auto wired service a service a;public service a get service a(){ return service a;}}测试,分别调用它们的get方法,都可以正常获取bean,可见循环依赖是可以实现的:
com . hydra . service . service b @ 58fd 99 com . hydra . service . service a @ 6b 1274d 2首先回顾一下上一篇文章中提到的bean实例化的过程。以下内容更依赖于spring的bean实例化源代码。如果你不熟悉,建议你花点时间看看上一篇文章。
在AbstractTautowireCapableBeanfactory的doCreateBean方法中,调用createBeanInstance方法创建原生对象,然后调用populateBean方法填充属性,最后调用各种回调方法和后处理器。
但是,在执行populateBean方法之前,上一篇文章中省略了一些与循环依赖相关的内容。看一下下面的代码:
上面的代码先做一个判断:如果当前的singleton bean是创建的,并且允许循环依赖,并且是在创建过程中,那么执行下面的addSingletonFactory方法。
主要工作是
将lambda表达式代表的ObjectFactory
,放入三级缓存的Map中。注意这里只是一个存放的操作,并没有实际执行lambda表达式中的内容,具体调用过程是在后面调用ObjectFactory的getObject方法时调用。这个方法执行完成后,三级缓存中存放了一条serviceA
的数据,二级缓存仍然为空。
回到正常调用流程,生成原生对象后,调用populateBean
方法进行属性的赋值也就是依赖注入,具体是通过执行AutowiredAnnotationBeanPostProcessor
这一后置处理器的postProcessPropertyValues
方法。
在这一过程中,serviceA
会找到它依赖的serviceB
这一属性,当发现依赖后,会调用DefaultListableBeanFactory
的doResolveDependency
方法,之后执行resolveCandidate
方法,在该方法中,尝试使用beanFactory
获取到serviceB
的bean实例。
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) throws BeansException { return beanFactory.getBean(beanName); }这时和之前没有循环依赖时的情况就会有些不一样了,因为现在
serviceB
还没有被创建出来,所以通过beanFactory
是无法直接获取的。因此当在doGetBean
方法中调用getSingleton
方法会返回一个null值:
因此,继续使用与之前相同的创建bean的流程,实例化serviceB
的bean对象。当serviceB
的原生对象被实例化完成后,同样可以看到它依赖的serviceA
还没有被赋值:
创建完serviceB
的原生对象后,同样执行addSingletonFactory
方法,将serviceB
放入三级缓存中,执行完成后,三级缓存中就已经存在了两个bean的缓存:
向下执行,serviceB
会调用populateBean
方法进行属性填充。和之前serviceA
依赖serviceB
相同的调用链,执行到resolveCandidate
方法,尝试使用beanFactory
的getBean
去获取serviceA
。
向下执行,调用getSingleton
方法尝试直接获取serviceA
,此时三级缓存singletonFactories
中我们之前已经存进去了一个key为serviceA
的beanName
,value为lambda表达式,这时可以直接获取到。
在执行singletonFactory
的getObject
方法时才去真正执行lambda表达式中的方法,实际执行的是getEarlyBeanReference
方法:
在遍历后置处理器后,获取到serviceA
的执行过后置处理器后的对象,执行:
this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);这里将
serviceA
放入二级缓存earlySingletonObjects
,并从三级缓存singletonFactories
中移除。在这一步执行完后,三级缓存中的serviceA
就没有了。 当我们从缓存中获取了serviceA
的bean后,就不会再调用createBean
去重复创建新的bean了。之后,顺调用链返回serviceB
调用的doResolveDependency
方法:
serviceB
就成功获取到了它的依赖的serviceA
属性的bean对象,回到inject
方法,使用反射给serviceA
赋值成功。
回到doCreateBean
的方法,可以看到serviceB
的serviceA
属性已经被注入了,但是serviceA
中的serviceB
属性还是null
。说明serviceB
的依赖注入已经完成,而serviceA
的依赖注入还没做完。
现在我们梳理一下运行到这里的流程:
1、在serviceA
填充属性过程中发现依赖了serviceB
,通过beanFactory
的getBean
方法,尝试获取serviceB
2、serviceB
不存在,执行了一遍serviceB
的创建流程,填充属性时发现serviceA
已经存在于三级缓存,直接注入给serviceB
可以看到,在创建serviceA
的过程中发现依赖的serviceB
不存在,转而去创建了serviceB
,而创建serviceA
的流程并没有执行完,因此在创建完serviceB
后再顺调用链返回,直到doResolveDependency
方法:
可以看到,需要依赖的serviceB
已经被创建并返回成功,返回到inject
方法,同样通过反射给serviceB
赋值:
返回doCreateBean
方法,可以看到serviceA
和serviceB
之间的循环依赖已经完成了:
这样,一个最简单的循环依赖流程就结束了。有的小伙伴可能会提出疑问,这样的话,我只需要添加一个缓存存放原生对象就够了啊,为什么还需要二级缓存和三级缓存两层结构呢?这个问题,我们放在下一篇具体讨论,看看循环依赖的具体实现时怎样的。
到此这篇关于Spring源码解析之循环依赖的实现流程的文章就介绍到这了,更多相关Spring循环依赖内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。