Spring源码分析之IOC循环依赖详解

Spring源码分析之IOC循环依赖详解

认真写文章,用心做分享。公众号:Java耕耘者文章都会在里面更新,整理的资料也会放在里面。

1.什么是循环依赖?

当多个Bean相互依赖时则构成了循环依赖,例如A,B两个Bean。其中A中存在属性B,B中存在属性A,当Spring在实例化A时发现A中存在属性B,就去实例化B,实例化B时又发现存在属性A,一直在循环注入依赖,导致循环依赖问题出现。

2.Spring是怎么解决循环依赖的?

Spring中会通过各种Bean中间状态来达到Bean还未实例化完成时提前将Bean提前注入到依赖Bean的属性中,假设说Bean有三种状态分别是青年态、胚胎态、小蝌蚪态其中青年态代表Bean已经实例化完成,可以直接使用了,胚胎态代表Bean已经存在了但是还在创建中,还未创建完毕,小蝌蚪态代表还未开始创建,但是随时可以进行创建,三个状态就类似于三个等级,可以逐步提升从小蝌蚪状态提升到胚胎状态然后再提升到青年态,然后Spring开始创建Bena时会提前将Bean存放到小蝌蚪态的缓存集合中,当发现存在循环依赖时会使用存在于小蝌蚪状态缓存集合中的Bean,提前

3.循环依赖的案例

假设例子中存在BeanA、BeanB、BeanC、BeanD四个Bean,其中

BeanA依赖着BeanB,CBeanB依赖着BeanCBeanC依赖着BeanABeanD依赖着BeanA,B,C

BeanAbeanA=beanFactory.getBean;BeanBbeanB=beanFactory.getBean;BeanCbeanC=beanFactory.getBean;BeanDbeanD=beanFactory.getBean;

那么他们的实例化流程是什么样的呢?

发现了吧,Spring解决循环依赖的法宝就是缓存。

3.代码解析

1.检查缓存中是否已经存在实例化完毕的Bean

protectedObjectgetSingleton{//首先检查一级缓存中是否存在ObjectsingletonObject=this.singletonObjects.get;/***如果一级缓存中不存在代表当前Bean还未被创建或者正在创建中*检查当前Bean是否正处于正在创建的状态中*/if){synchronized{//检查二级缓存中是否存在singletonObject=this.earlySingletonObjects.get;/***@@如果二级缓存中不存在并且允许使用早期依赖*allowEarlyReference:它的含义是是否允许早期依赖*@@那么什么是早期依赖?*就是当Bean还未成为成熟的Bean时就提前使用它,在实例化流程图中我们看到在添加缓存前刚刚实例化Bean但是还未依赖注入时的状态*/if{//获取三级缓@AnsonC@SEO@存中的BeanObjectFactoryObjectFactorysingletonFactory=this.singletonFactories.get;//如果Bean对应的ObjectFactory存在if{//使用getObject方法获取到Bean的实例singletonObject=singletonFactory.getObject;//将bean从三级缓存提升至二级缓存this.earlySingletonObjects.put;this.singletonFactories.remove;}}}}returnsingletonObject;}

2.创建Bean

protectedObjectdoCreateBeanthrowsBeanCreationException{/***我们一开始通过getSingleton方法中获取三级缓存中存放的Bean,这里就是向三级缓存中添加bean的地方*流程:*1.检查当前bean是否为单例模式,并且是否允许循环引用[讲解1],并且当前是否正在创建中*2.如果允许提前曝光[讲解2],addSingletonFactory方法向缓存中添加当前bean的ObjectFactory**[讲解1]:当前Bean如果不允许循环引用,则这里就不会提前曝光,对应的ObjectFactory*则当发生循环依赖时会抛出BeanCreationException异常**[讲解2]:提前曝光的含义就是说当bean还未创建完毕时就先将创建中状态的bean放到指定缓存中,为循环依赖提供支持*/booleanearlySingletonExposure=this.allowCircularReferencesisSingletonCurrentlyInCreation);//需要提前曝光if{/***向缓存中添加当前bean的ObjectFactory*/addSingletonFactory-getEarlyBeanReference);}/***Initializethebeaninstance.*初始化Bean实例阶段*/ObjectexposedObject=bean;try{/***依赖注入这时会递归调用getBean*/populateBean;//调用初始化方法,如:init-methodexposedObject=initializeBean;}/***当允许提前曝光时进入判断*@这里做了什么?*1.检查当前bean是否经历了一场循环依赖*-通过getSingleton获取缓存中的bean,传入false代表不获取三级缓存中的bean*-为什么说检查当前bean是否经历了一场循环依赖呢?因为上述说了传入false代表不获取三级缓存的*-那么什么情况下才会存在与一级缓存和二级缓存呢?答案就是循环依赖后[解释1]和bean实例化完成后*-所以如果getSingleton返回的bean不为空,则这个bean就是刚刚经历了循环依赖**2.检查提前曝光的bean和当前的Bean是否一致*-下面有个判断if,这个判断从缓存中获取的bean和经历过初始化后的bean*-是否一致,可能我们有点晕,这里解释一下,缓存从的bean是什么时候存进去的?是在addSingletonFactory方法*-然后这里存进去的bean只是提前曝光的bean,还没有依赖注入和初始化,但是在依赖注入和初始化时都是可能直接改变*-当前bean的实例的,这意味着什么?意味着经历了依赖注入和初始化的bean很可能和缓存中的bean就已经完全不是一个bean了*下面讲解当一致或不一致时的逻辑:*2.1一致:*不是很理解,直接赋值,可是经历了各种BeanPostProsser或者依赖注入和初始化后不是就不一样了吗*2.2不一致:*看下方对于elseif代码块的解释**@[解释1]*当循环依赖时,A依赖着B,B依赖着A,实例化A首先将A放到三级缓存中然后发现依赖着B,然后去实例化B,发现依赖着A*发现A在三级缓存,然后获取三级缓存中的bean并且将A从三级缓存中提升到二级缓存中,实例化B完成,接着实例化A也完成。**@通俗讲解*假设我们业务上对某种数据加了缓存,假设i在缓存中存的值为1,当我在数据库中把i的值改成2时,缓存中的i还没有被改变还是1*这时的数据已经和我们的真实数据偏离了,不一致了,这时有两种解决方式:1.服务器检查到数据不一致抛出异常。*2.直接使用原始值也就是1,当然这两种方式明显不是我们正常数据库的操作,只是*为了说明当前的这个例子而已。**/if{//获取缓存中beanName对应的beanObjectearlySingletonReference=getSingleton;//当经历了一场循环依赖后earlySingletonReference就不会为空if{//如果exposedObject没有在初始化方法中被改变,也就是没有被增强if{//直接赋值?可是经历了各种BeanPostProsser或者依赖注入和初始化后不是就不一样了吗exposedObject=earlySingletonReference;}/****走到elseif时说明当前Bean被BeanPostProessor增强了*判断的条件为:*1.如果允许使用被增强的*2.检查是否存在依赖当前bean的bean**如果存在依赖的bean已经被实例化完成的,如果存在则抛出异常*为什么抛出异常呢?*因为依赖当前bean的bean已经在内部注入了当前bean的旧版本,但是通过初始化方法后这个bean的版本已经变成新的了*旧的哪个已经不适用了,所以抛出异常**/elseif){String[]dependentBeans=getDependentBeans;SetactualDependentBeans=newLinkedHashSet;for{if){a@AnsonB@SEO@ctualDependentBeans.add;}}if){thrownewBeanCurrentlyInCreationException+"]initsrawversionaspartofacircularreference,buthaseventuallybeen"+"wrapped.Thismeansthatsaidotherbeansdonotusethefinalversionofthe"+"bean.Thisisoftentheresultofover-eagertypematching-considerusing"+"'getBeanNamesOfType'withthe'allowEagerInit'flagturnedoff,forexample.");}}}}try{/***Registerbeanasdisposable.*注册Bean的销毁方法拓展*/registerDisposableBeanIfNecessary;}catch{thrownewBeanCreationException,beanName,"Invaliddestructionsignature",ex);}returnexposedObject;}结束语关注私信回复:555领取Java高级架构资料、Spring源码分析、Dubbo、Redis、Netty、zookeeper、Springcloud、分布式等

(责任编辑:上游棋牌app)

本文地址:/chuanyue/20200606/5861.html

上一篇:特斯拉的2020年:我在中国大丰收,你们要加油哦 下一篇:年产值超万亿上游棋牌app的印刷业,为何大蛋糕还不够分?