当前位置: 首页 > 后端技术 > Java

springioc(下):循环依赖、三级缓存和FactoryBean

时间:2023-04-01 18:48:49 Java

上一篇从整体上介绍了springioc容器初始化流程(starter->refresh->registerBeanDefinition->doCreateBean->populateBean),但仅针对常规流程。一些特殊情况,比如spring会如何处理“对象循环申请”——这是本文要回答的问题。1、spring是如何解决对象的循环依赖问题的?注意:构造函数类型的循环依赖,spring会直接抛出异常。这里的重点是属性注入的循环依赖。什么是循环依赖?@ComponentclassA{@AutowireBb;}@ComponentclassB{@AutowireAa;}上面的代码是一个比较简单的循环依赖:A引用B的同时,B也引用了A,初始化的时候会发生什么?问题?创建A时,发现引用了B,于是去创建B;创建B的时候,发现引用了A,就去创建A,导致死循环。。。怎么解决?解决方法也很简单,只要声明一个集合(如:creatingSet),当需要创建对象时,先检查集合中是否有,如果没有,重新创建,将对象放入集合。这样A和B对象的初始化过程就变成了:检查creatingSet中是否有对象A,没有就创建A,记录creatingSet.set(A)发现A引用了B反而创建了B,没有creatingSetB中的对象。创建并记录creatingSet.set(B),发现B引用了A,此时creatingSet中已有对象A,无需直接创建和赋值。春天也是同样的想法。它通过singletonsCurrentlyInCreation集合记录正在创建的对象名称,使用三级缓存存储对象。spring是怎么解决的?以一个更简单的循环依赖为例:@ComponentclassA{@AutowireAa1;}看一下A对象的初始化过程getBean(beanName){//--1.添加标记[singletonsCurrentlyInCreation.set(beanName)]beforeSingletonCreation(beanName);//--2.构造函数创建对象【创建了一个对象,其属性a1为空】instanceWrapper=createBeanInstance(beanName,mbd,args);//--3.放入三级Cache,移除二级[三级缓存中a1的工厂]//##这里只会执行循环依赖,代理动作高级addSingletonFactory(beanName,()->getEarlyBeanReference(beanName,mbd,bean));//--4.属性初始化,递归调用getSingleton(String,boolean)//[初始化属性a1开始]populateBean();?????ObjectgetSingleton(StringbeanName,booleanallowEarlyReference)ObjectgetSingleton(StringbeanName,booleanallowEarlyReference){//==a.尝试从一级缓存中获取一个ObjectsingletonObject=this.singletonObjects.get(beanName);//==乙。一级缓存没有&&但是正在创建(递归时会进入if)if(singletonObject==null&&isSingletonCurrentlyInCreation(beanName)){//**通过三级缓存获取的工厂创建singletonObject=singletonFactory.getObject();//添加第二层,移除第三层this.earlySingletonObjects.put(beanName,singletonObject);this.singletonFactories.remove(beanName);}//==c.递归时,执行`代码2`中的If,返回二级缓存对象returnsingletonObject;}?????//【初始化属性a1结束:二级缓存中存在a1对象,移除三级缓存中的a1工厂】populateBean();//##4.5调用对象后处理器【代码3已经提前做了代理,这里不执行】//##initializeBean(beanName,exposedObject,mbd)//--5.注入property[一个对象的a1属性有一个值]applyPropertyValues(beanName,mbd,bw,pvs);//--6.去掉标记[singletonsCurrentlyInCreation.remove(beanName)]afterSingletonCreation(beanName);//--7.去掉第三级,去掉第二级,将对象存入一级缓存【撒花结束,A对象初始化完成】addSingleton(beanName,singletonObject);代码3、代码4和代码4.5在三个地方进行了更改。以上就是spring解决对象循环申请问题的方式。2、spring为什么要设计二级缓存和三级缓存来解决循环依赖?这个问题其实可以分为两个问题。A.只使用二级缓存:代码3处直接创建代理对象可行吗?答:可以,但是不符合spring哲学。spring的思想是:Bean在生命周期的最后一步完成代理,而不是实例化后立即完成代理。一般来说,解决循环依赖有两种方式:不管是否存在循环依赖,提前创建代理对象,并将代理对象放入缓存;当发生循环依赖时,直接从缓存中获取。本节讨论的问题——只有二级缓存:代码3直接创建代理对象——就是这种方式。就算再粗糙一点,二级缓存也省了,放到一级缓存也能解决。(你想,反正这些对象最终都会被加载)代理对象并不是预先创建的,只有在发生循环依赖时才会生成代理对象。很显然,spring选择了这样的方式。B、只使用三级缓存,每次都通过工厂创建是否可行?答:不是。这个比较容易理解。每次通过工厂创建对象时,单例都会被销毁。3.什么是FactoryBean?除了普通的对象,spring还为用户提供了FactoryBean接口来创建复杂的对象。(可以看做是一个扩展点)如果在spring中注入一个FactoryBean对象,从SpringContext中获取的是一个普通的调用FactoryBean.getObject()创建的对象。有点乱,我举个例子说明一下://DeclareaTestobjectclassTest{}//DeclareaTestFactoryBeanobjecttoimplementFactoryBeaninterfaceclassTestFactoryBeanimplementsFactoryBean{@OverridepublicCostFormgetObject()throwsException{测试测试=新测试();返回测试;}@OverridepublicClassgetObjectType(){returnTest.class;}}上面的例子中,通过SpringContextUtil.getBean("testFactoryBean")获取的对象不是TestFactoryBean,而是Test。那么如何获取TestFactoryBean,只需要在名字上加上“&”前缀即可。TestFactoryBeanfactoryBean=SpringContextUtil.getBean("&testFactoryBean")测试测试=SpringContextUtil.getBean("testFactoryBean");为什么源码实现是这样的?通过源码寻找答案。TestFactoryBean对象的注入//对象初始化过程中有这样的逻辑org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletonspreInstantiateSingletons(){//如果是FactoryBean对象,会加上"&"前缀注入if(isFactoryBean(beanName)){对象bean=getBean("&"+beanName);}}测试对象注入Spring对FactoryBean对象处理的执行入口在这里://前面分析的对象初始化逻辑sharedInstance=getSingleton(beanName,()->{returncreateBean(beanName,mbd,args);});//##创建对象后,做特殊处理bean=getObjectForBeanInstance(sharedInstance,name,beanName,mbd);观察做了什么处理:ObjectgetObjectForBeanInstance(ObjectbeanInstance,Stringname,StringbeanName,@NullableRootBeanDefinitionmbd){//--非FactoryBean对象,直接返回if(!(beanInstanceinstanceofFactoryBean)||BeanFactoryUtils.isFactoryDe??reference(name)){返回beanInstance;}//--通过FactoryBean获取对象FactoryBeanfactory=(FactoryBean)beanInstance;object=getObjectFromFactoryBean(工厂,beanName,!synthetic);??????//##最终会调用这里,执行FactoryBeanobject的getObject方法=factory.getObject();??????object=getObjectFromFactoryBean(factory,beanName,!synthetic);}附录P6-P7知识库

最新推荐
猜你喜欢