专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

深度解析Spring是如何解决循环依赖的

  问题:什么是循环依赖呢?
  就是一个或者多个bean之间互相引用对方,这种依赖关系最终形成一个闭环就是循环依赖。循环依赖有这以下几种情况:自己依赖自己
  两个对象互相依赖
  多个对象间接依赖
  问题:循环依赖有几种场景呢?构造器注入publicclassA{privateBb;通过构造方法注入BpublicA(Bb){System。out。println(bb);}publicvoidtest(){System。out。println(属性Bb);}}publicclassB{privateAa;通过构造方法注入ApublicB(Aa){System。out。println(aa);}publicvoidtest(){System。out。println(属性Aa);}}xml配置beanclasscom。gongj。circularDependencies。dto。Aidaconstructorargindex0refbconstructorargbeanbeanclasscom。gongj。circularDependencies。dto。Bidbconstructorargindex0refaconstructorargbean复制代码启动类publicstaticvoidmain(String〔〕args){ClassPathXmlApplicationContextcontextnewClassPathXmlApplicationContext(circularDependencies。xml);Aa(A)context。getBean(a);a。test();}结果:抛出BeanCreationException异常复制代码单例的setter注入publicclassA{privateBb;publicBgetBb(){returnb;}publicvoidsetBb(Bb){this。bb;}publicvoidtest(){System。out。println(属性Bb);}}publicclassB{privateAa;publicAgetAa(){returna;}publicvoidsetAa(Aa){this。aa;}publicvoidtest(){System。out。println(属性Aa);}}xml配置:beanclasscom。gongj。circularDependencies。dto。Aidapropertynamebbrefbpropertybeanbeanclasscom。gongj。circularDependencies。dto。Bidbpropertynameaarefapropertybean结果:属性Bcom。gongj。circularDependencies。dto。B19d37183复制代码dependson指定依赖beanclasscom。gongj。circularDependencies。dto。Aidadependsonbpropertynamebbrefbpropertybeanbeanclasscom。gongj。circularDependencies。dto。Bidbdependsonapropertynameaarefapropertybean结果:抛出BeanCreationException异常复制代码原型的setter注入beanclasscom。gongj。circularDependencies。dto。Aidascopeprototypepropertynamebbrefbpropertybeanbeanclasscom。gongj。circularDependencies。dto。Bidbscopeprototypepropertynameaarefapropertybean结果:抛出BeanCreationException异常复制代码
  从上面的几个例子可以得知:
  对于构造器的循环依赖,Spring是无法自动帮我们解决的,会抛出BeanCurrentlyInCreationException异常,表示循环依赖。当然可以由我们程序员解决,下面会讲到的。
  Spring帮我们解决的循环依赖是单例的settet注入的循环依赖。对于作用域为prototype的循环依赖,Spring也是不会解决的,直接抛出BeanCurrentlyInCreationException异常。
  使用dependson属性指定依赖,造成循环依赖,Spring也不会对此进行解决,直接抛出BeanCurrentlyInCreationException异常。
  那么循环依赖是个问题吗?如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情。那在Spring中为什么会有循环依赖这种问题产生呢?那是因为在Spring中,一个对象并不是简单new出来的,而是会经过一系列的Bean的生命周期,就是因为Bean的生命周期所以才会出现循环依赖问题。当然,在Spring中,出现循环依赖的场景很多,有的场景Spring帮我们解决了,而有的场景则需要程序员来解决,下文详细来说Spring是如何解决循环依赖的,也就是为什么允许单例的setter循环依赖注入。Bean的生命周期
  这里不会对Bean的生命周期进行详细的描述,只描述一下大概的过程。想详细了解可以看看笔者前面的源码阅读。如果想真正的理解循环依赖,那对Bean的生命周期肯定要有了解的。
  Bean的生命周期指的就是:在Spring中,Bean是如何生成的?
  Bean的生成步骤如下:
  可以看到,对于Spring中的Bean的生成过程,步骤很多。
  但可以发现,在Spring中,创建一个Bean,是在实例化这步得到的。推断构造方法之后根据构造方法反射得到一个对象的,当然还有其他方式得到一个对象,比如说工厂方法、Supplier回调,不管其他方式如何创建,反正创建对象出来的这一步叫实例化,而创建出来的这个对象我们称之为原始对象!
  得到一个原始对象后,Spring需要给对象中的属性进行依赖注入,那么这个注入过程是怎样的?
  比如上文说的A类,A类中存在一个B类的b属性,所以,当A类生成了一个原始对象之后,就会去给b属性去赋值,此时就会根据b属性的类型和属性名(beanName)去BeanFactory中去获取B类所对应的单例bean。如果此时BeanFactory中存在B对应的Bean,那么直接拿来赋值给b属性;如果此时BeanFactory中不存在B对应的Bean,则需要生成一个B对应的Bean,然后赋值给b属性。
  问题就出现在第二种情况,如果此时B类在BeanFactory中还没有生成对应的Bean,那么就需要去生成,就会经过B的Bean的生命周期。
  那么在创建B类Bean的过程中,B类中存在一个A类的a属性,那么在创建B类Bean的过程中就需要A类对应的Bean,B类就会去创建A,所以这里就出现了循环依赖:
  ABean创建依赖了B属性触发BBean创建B依赖了A属性需要ABean(但ABean还在创建过程中)
  从而导致ABean创建不出来,BBean也创建不出来。
  简化一下生命周期,将重要的部分列出来:A的生命周期1。实例化A对象(newA()),称之为原始对象2。填充b属性(走getBean流程)从单例池中获取对应的bean找不到创建B对应的Bean(走B的生命周期)3。填充其他属性4。初始化后5。添加单例池B的生命周期1。实例化B对象(newB()),称之为原始对象2。填充a属性(走getBean流程)从单例池中获取对应的bean找不到创建A对应的Bean(走A的生命周期)3。填充其他属性4。初始化后5。添加单例池复制代码
  这就是循环依赖,但是上文说了,Spring通过某些机制帮开发者解决了单例setter注入循环依赖的问题,这个机制就是三级缓存。问题:什么是三级缓存?
  Spring使用三级缓存解决循环依赖!singletonObjects:一级缓存,用于保存经历了完整生命周期的bean对象,也就是成品对象,最终给程序员使用的对象。earlySingletonObjects:二级缓存,比singletonObjects多了一个early,early早期,用于保存实例化完成的bean实例,Bean的生命周期还没走完就把这个Bean实例放入了earlySingletonObjects中,属于半成品对象。singletonFactories:三级缓存,缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的。主要是解决SpringAOP。问题:一定要三级缓存吗?
  就上述出现的循环依赖,一定要用三级缓存才能解决吗?再增加一个Map不就解决了吗?一个缓存用于存放成品对象,另外一个缓存用于存放半成品对象。没有AOP的循环依赖
  A的生命周期1。实例化A对象(newA()),称之为原始对象原始对象放入gongJMap中进行缓存2。填充b属性(走getBean流程)从单例池中获取b对应的bean找不到创建B对应的Bean(走B的生命周期)3。填充其他属性4。初始化后5。添加单例池B的生命周期1。实例化B对象(newB()),称之为原始对象原始对象放入gongJMapb,B原始对象中进行缓存2。填充a属性(走getBean流程)从单例池中获取a对应的bean找不到去gongJMap获取得到A原始对象3。填充其他属性4。初始化后5。添加单例池复制代码
  可以看到二级缓存就能解决上述的循环依赖(不进行AOP时)。那Spring为什么要使用三级缓存呢?其实主要是4。初始化后这步会可能进行AOP操作。AOP操作会根据你的原始对象产生一个代理对象。生命周期再次发生变化,需要进行AOPA的生命周期1。实例化A对象(newA()),称之为原始对象原始对象放入gongJMap中进行缓存2。填充b属性(走getBean流程)从单例池中获取b对应的bean找不到创建B对应的Bean(走B的生命周期)3。填充其他属性4。初始化后(AOP)产生代理对象5。添加单例池B的生命周期1。实例化B对象(newB()),称之为原始对象原始对象放入gongJMapb,B原始对象中进行缓存2。填充a属性(走getBean流程)从单例池中获取a对应的bean找不到去gongJMap获取得到A原始对象3。填充其他属性4。初始化后(AOP)产生代理对象5。添加单例池复制代码
  各位可以思考一下上面这个步骤有没有问题?1。实例化A对象(newA()),称之为原始对象原始对象放入gongJMap中进行缓存2。填充b属性(走getBean流程)从单例池中获取b对应的bean找不到创建B对应的Bean(走B的生命周期)3。实例化B对象(newB()),称之为原始对象原始对象放入gongJMapb,B原始对象中进行缓存4。填充a属性(走getBean流程)从单例池中获取a对应的bean找不到去gongJMap获取得到A原始对象5。填充其他属性6。初始化后(AOP)产生代理对象7。添加单例池(将B代理对象放到单例池)B生命周期结束走A的生命周期A的b属性放的是B的代理对象8。填充其他属性9。初始化后(AOP)产生代理对象10。添加单例池(将A代理对象放到单例池)复制代码
  在第4步进行填充a属性拿到的是A的原始对象。而在第2步填充b属性,A里的b属性放的是B的代理对象。造成两者不一致了,正常情况需如下:
  A的b属性填充的是B的代理对象,B的a属性填充的是A的代理对象。这才是正常的。那怎么解决呢!终于需要用到三级缓存了吗?不不不,还用不到,在这里还是可以使用二级解决的。办法:提前进行AOP处理,然后在初始化后那步进行判断,如果进行过AOP操作就不再进行AOP了。A的生命周期1。实例化A对象(newA()),称之为原始对象提前进行AOP,产生代理对象放入gongJMap中进行缓存2。填充b属性(走getBean流程)从单例池中获取b对应的bean找不到创建B对应的Bean(走B的生命周期)3。填充其他属性4。初始化后(AOP),判断是否进行过AOP了4。5从gongJMap获取A的代理对象5。添加单例池(A代理对象)B的生命周期1。实例化B对象(newB()),称之为原始对象提前进行AOP,产生代理对象放入gongJMapb,B代理对象中进行缓存2。填充a属性(走getBean流程)从单例池中获取a对应的bean找不到去gongJMap获取得到A代理对象3。填充其他属性4。初始化后(AOP),判断是否进行过AOP了4。5从gongJMap获取B的代理对象5。添加单例池(B代理对象)复制代码
  这种方式解决了A、B对象属性不一致的问题。那这里就会出现一个新的问题,什么情况下才需要提前进行AOP呢?答案是产生循环依赖的时候才需要提前进行AOP。因为如果没有出现循环依赖,A、B两个Bean各自走自己的生命周期那是不会有影响的。A的生命周期1。实例化A对象(newA()),称之为原始对象原始对象放入gongJMap中进行缓存2。填充属性3。初始化后(AOP)产生A代理对象4。添加单例池(A代理对象)B的生命周期1。实例化B对象(newB()),称之为原始对象原始对象放入gongJMapb,B原始对象中进行缓存2。填充属性3。初始化后(AOP)产生B代理对象4。添加单例池(B代理对象)复制代码
  那又会有一个问题,我要才能怎么知道你产生了循环依赖呢?还是上述例子,A依赖B,B依赖A。在创建A的时候能知道有循环依赖吗?那肯定不能!填充b属性的时候?不能!实例化B?不能。只有在B实例化之后进行a属性填充时,才能知道A、B发生了循环依赖。
  解决:增加一个Set集合,用来存取正在创建的beanName。生命周期再次发生变化,如下:A的生命周期0。将a放入Set中1。实例化A对象(newA()),称之为原始对象2。填充b属性(走getBean流程)从单例池中获取b对应的bean找不到创建B对应的Bean(走B的生命周期)3。填充其他属性4。初始化后(AOP)4。5从gongJMap获取A的代理对象5。添加单例池(A代理对象)B的生命周期0。将b放入Set中1。实例化B对象(newB()),称之为原始对象2。填充a属性(走getBean流程)从单例池中获取a对应的bean找不到判断a是否正在创建中正在创建中,代表出现了循环依赖gongJMap寻找找不到进行AOP,产生代理对象得到A代理对象gongJMap中进行缓存3。填充其他属性4。初始化后(AOP)4。5从gongJMap获取B的代理对象5。添加单例池(B代理对象)复制代码
  看到这,不知道各位有没有疑问,原始对象我们没有将它缓存起来了。但前面又讲过,代理对象需要根据原始对象去创建。所以这时候才需要用到第三级缓存了。A的生命周期0。将a放入Set中,标记为当前单例bean正在创建中1。实例化A对象(newA()),称之为原始对象放入第三级缓存beanName:A原始对象2。填充b属性(走getBean流程)从单例池中获取b对应的bean找不到判断b是否正在创建中(B没有在创建中)创建B对应的Bean(走B的生命周期)3。填充其他属性4。初始化后(AOP)4。5从gongJMap获取A的代理对象5。添加单例池(A代理对象)B的生命周期0。将b放入Set中,标记为当前单例bean正在创建中1。实例化B对象(newB()),称之为原始对象放入第三级缓存beanName:B原始对象2。填充a属性(走getBean流程)从单例池中获取a对应的bean找不到判断a是否正在创建中正在创建中,代表出现了循环依赖从gongJMap寻找找不到从第三级缓存寻找得到A原始对象进行AOP,产生代理对象得到A代理对象gongJMap中进行缓存3。填充其他属性4。初始化后(AOP)4。5从gongJMap获取B的代理对象5。添加单例池(B代理对象)复制代码
  到此,Spring为什么要使用三级缓存去解决循环依赖的思路就到这了。接下来我们就开始阅读源码,看看Spring设计的是否与我们推断是否一致。源码阅读
  源码阅读呢会涉及到几个方法。我们根据上述所说的Bean的生命周期来分析。第零步
  该方法位于DefaultSingletonBeanRegistrygetSingleton(StringbeanName,ObjectFactorylt;?singletonFactory)。
  创建A对象,首先从单例池拿,有直接返回。没有的话首先将A添加到正在创建Bean的集合中,然后进行创建A逻辑。publicObjectgetSingleton(StringbeanName,ObjectFactorylt;?singletonFactory){Assert。notNull(beanName,Beannamemustnotbenull);synchronized(this。singletonObjects){去单例池中获取实例ObjectsingletonObjectthis。singletonObjects。get(beanName);如果不存在实例,则创建单例bean实例if(singletonObjectnull){忽略把当前正在创建的beanName添加到singletonsCurrentlyInCreation中,singletonsCurrentlyInCreation是一个Set表示这些bean正常创建中,在没创建完时不能重复创建beforeSingletonCreation(beanName);booleannewSingletonfalse;booleanrecordSuppressedExceptions(this。suppressedExceptionsnull);if(recordSuppressedExceptions){this。suppressedExceptionsnewLinkedHashSet();}try{singletonFactory是外面传进来的lambda表达式,执行lambda表达式就是调用createBean()singletonObjectsingletonFactory。getObject();newSingletontrue;}忽略将创建好的单例bean添加到单例池singletonObjects中和单例注册表registeredSingletons中并清除二级、三级缓存if(newSingleton){addSingleton(beanName,singletonObject);}}returnsingletonObject;}}protectedvoidbeforeSingletonCreation(StringbeanName){inCreationCheckExclusions中的beanName,表示如果是这些bean正在创建中,重复创建也没关系singletonsCurrentlyInCreation中的beanName,表示这些bean正常创建中,在没创建完时不能重复创建if(!this。inCreationCheckExclusions。contains(beanName)!this。singletonsCurrentlyInCreation。add(beanName)){thrownewBeanCurrentlyInCreationException(beanName);}}复制代码第一步1。实例化A对象(newA()),称之为原始对象放入第三级缓存beanName,A原始对象复制代码
  关注一下2、实例化,这里会产生一个原始对象。该方法位于AbstractAutowireCapableBeanFactory。protectedObjectdoCreateBean(finalStringbeanName,finalRootBeanDefinitionmbd,finalNullableObject〔〕args)throwsBeanCreationException{省略代码2、实例化if(instanceWrappernull){创建bean实例1、工厂方法2、构造函数自动注入3、简单初始化instanceWrappercreateBeanInstance(beanName,mbd,args);}包装的实例对象,也就是原始对象finalObjectbeaninstanceWrapper。getWrappedInstance();省略代码4、如果当前bean是单例并且支持循环依赖,且当前bean正在创建,就通过往singletonFactories(三级缓存)添加一个objectFactory,这样后期如果有其他bean依赖该bean可以从singletonFactories获取到bean,解决循环依赖getEarlyBeanReference()可以对返回的bean进行修改,这边目前除了可能会返回代理对象其他的都是直接返回beanbooleanearlySingletonExposure(mbd。isSingleton()this。allowCircularReferencesisSingletonCurrentlyInCreation(beanName));if(earlySingletonExposure){if(logger。isTraceEnabled()){logger。trace(EagerlycachingbeanbeanNametoallowforresolvingpotentialcircularreferences);}构造一个ObjectFactory添加到singletonFactories中addSingletonFactory(beanName,()getEarlyBeanReference(beanName,mbd,bean));}Initializethebeaninstance。ObjectexposedObjectbean;try{5、填充属性Autowired,将各个属性注入对bean进行填充将各个属性注入populateBean(beanName,mbd,instanceWrapper);6、执行初始化方法exposedObjectinitializeBean(beanName,exposedObject,mbd);}catch(Throwableex){if(exinstanceofBeanCreationExceptionbeanName。equals(((BeanCreationException)ex)。getBeanName())){throw(BeanCreationException)ex;}else{thrownewBeanCreationException(mbd。getResourceDescription(),beanName,Initializationofbeanfailed,ex);}}7、if(earlySingletonExposure){earlySingletonReference只有在检测到有循环依赖的情况下才会不为空ObjectearlySingletonReferencegetSingleton(beanName,false);if(earlySingletonReference!null){如果提前暴露的对象(bean)和经过了完整的生命周期后的对象相等(exposedObject)则把缓存中的earlySingletonReference赋值给exposedObject最终会添加到singletonObjects中去(初始化之后的bean等于原始的bean,说明不是proxy),if(exposedObjectbean){exposedObjectearlySingletonReference;}检测该bean的dependon的bean是否都已经初始化好了elseif(!this。allowRawInjectionDespiteWrappinghasDependentBean(beanName)){String〔〕dependentBeansgetDependentBeans(beanName);SetStringactualDependentBeansnewLinkedHashSet(dependentBeans。length);for(StringdependentBean:dependentBeans){if(!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)){actualDependentBeans。add(dependentBean);}}returnexposedObject;}复制代码
  我们再关注一下第四点,该代码所处的位置Bean进行实例化之后,属性填充之前。按照我们的理解是要放入到三级缓存的。4、如果当前bean是单例并且支持循环依赖,且当前bean正在创建,就通过往singletonFactories(三级缓存)添加一个objectFactory,这样后期如果有其他bean依赖该bean可以从singletonFactories获取到bean,解决循环依赖getEarlyBeanReference()可以对返回的bean进行修改,这边目前除了可能会返回动态代理对象其他的都是直接返回beanbooleanearlySingletonExposure(mbd。isSingleton()this。allowCircularReferencesisSingletonCurrentlyInCreation(beanName));if(earlySingletonExposure){if(logger。isTraceEnabled()){logger。trace(EagerlycachingbeanbeanNametoallowforresolvingpotentialcircularreferences);}构造一个ObjectFactory添加到singletonFactories中addSingletonFactory(beanName,()getEarlyBeanReference(beanName,mbd,bean));}复制代码
  要不要往三级缓存添加对象是由earlySingletonExposure这个变量控制的。三个值都为true时,earlySingletonExposure才为truembd。isSingleton():当前bean是单例allowCircularReferences:是否支持循环依赖,默认是trueisSingletonCurrentlyInCreation(beanName):当前bean是否正在创建中,创建中则返回true,至于在哪里被放入到Set集合中的,可以看前面的源码阅读。具体代码在DefaultSingletonBeanRegistry类的getSingleton方法。
  earlySingletonExposure这个变量的值,正常情况下都是为true的。所以addSingletonFactory方法都会被执行,那一起来看看它干了什么?
  addSingletonFactory方法两个入参,一个是beanName,一个是类型为ObjectFactory的函数式接口。protectedvoidaddSingletonFactory(StringbeanName,ObjectFactorylt;?singletonFactory){Assert。notNull(singletonFactory,Singletonfactorymustnotbenull);synchronized(this。singletonObjects){if(!this。singletonObjects。containsKey(beanName)){this。singletonFactories。put(beanName,singletonFactory);this。earlySingletonObjects。remove(beanName);this。registeredSingletons。add(beanName);}}}复制代码
  如果一级缓存singletonObjects,也就是单例池中不存在当前beanName的记录,则进入if代码块。首先往三级缓存singletonFactories中添加一个key为beanName,value为ObjectFactory的数据。然后从二级缓存earlySingletonObjects删除key为beanName的数据。再然后将beanName放入registeredSingletons的Set集合中。这段代码重点就是往第三级缓存里添加了一个key为beanName,value为ObjectFactory的函数式接口,也就是一个Lambda表达式。我们自己推断的只放一个原始对象。那我们来看看具体的Lambda表达式的实现!注意,这里并没有执行Lambda表达式的具体实现。可以看到这段代码比较符合我们所推测的,只不过Spring是往三级缓存里存的是类型为ObjectFactory的Lambda表达式而不是纯粹原始对象。第二步
  2、然后进行b属性填充从单例池中获取b对应的bean找不到判断b是否正在创建中(B没有在创建中,还没开始创建呢)创建B对应的Bean。
  最终会调用到getBean(beanName)方法。然后进入到doGetBean方法。protectedTTdoGetBean(finalStringname,NullablefinalClassTrequiredType,NullablefinalObject〔〕args,booleantypeCheckOnly)throwsBeansException{对name进行转换,找到真正的beanName如果传入的name是gongjFactoryBean,那么beanName就是gongjFactoryBeanfinalStringbeanNametransformedBeanName(name);Objectbean;根据beanName去单例池中获取BeanObjectsharedInstancegetSingleton(beanName);Mapif(sharedInstance!nullargsnull){if(logger。isTraceEnabled()){if(isSingletonCurrentlyInCreation(beanName)){logger。trace(ReturningeagerlycachedinstanceofsingletonbeanbeanNamethatisnotfullyinitializedyetaconsequenceofacircularreference);}else{logger。trace(ReturningcachedinstanceofsingletonbeanbeanName);}}判断sharedInstance是不是FactoryBean,如果是FactoryBean,那么真正需要拿到的是getObject方法所返回的对象beanName是spinrg进行解析后获取到的BeanNamename我们手动传入的beanNamesharedInstance根据beanName获取到的单例Bean对象beangetObjectForBeanInstance(sharedInstance,name,beanName,null);}单例池没有获取到Bean则走创建Bean的流程else{}复制代码
  上述代码在本篇中只需要关注getSingleton方法。OverrideNullablepublicObjectgetSingleton(StringbeanName){returngetSingleton(beanName,true);}复制代码
  注意getSingleton(beanName,true);的第二个参数值为true。NullableprotectedObjectgetSingleton(StringbeanName,booleanallowEarlyReference){从单例池中获取实例ObjectsingletonObjectthis。singletonObjects。get(beanName);如果从单例池中没有获取到实例并且指定的单例bean正在创建中if(singletonObjectnullisSingletonCurrentlyInCreation(beanName)){锁定全局变量并进行处理synchronized(this。singletonObjects){earlySingletonObjects二级缓存singletonObjectthis。earlySingletonObjects。get(beanName);没有从二级缓存中获取到并且allowEarlyReference为trueif(singletonObjectnullallowEarlyReference){实例化之后调用addSingletonFactory方法将ObjectFactory初始化存储在singletonFactoriesMap中singletonFactories三级缓存ObjectFactorylt;?singletonFactorythis。singletonFactories。get(beanName);if(singletonFactory!null){执行lambdaAOpsingletonObjectsingletonFactory。getObject();记录在缓存中earlySingletonObjects与singletonFactories互斥this。earlySingletonObjects。put(beanName,singletonObject);this。singletonFactories。remove(beanName);}}}}returnsingletonObject;}复制代码
  从单例池获取bBean,那是肯定获取不到的,所以singletonObject值为null。至于b是否正在创建中,那肯定也是false,b还没开始创建!所以if代码块都不会进入,该方法直接返回null。返回null也就代表着B需要进行创建。进入到B的生命周期。第三步
  开始创建BBean。流程如下:0。将b放入Set中,标记为当前单例bean正在创建中1。实例化B对象(newB()),称之为原始对象放入第三级缓存beanName:B原始对象2。填充a属性(走getBean流程)从单例池中获取a对应的bean找不到判断a是否正在创建中正在创建中,代表出现了循环依赖从gongJMap寻找找不到从第三级缓存寻找得到A原始对象进行AOP,产生代理对象得到A代理对象gongJMap中进行缓存复制代码单例池没有获取到Bean则走创建Bean的流程else{省略根据Scope去创建bean创建单例模式Bean的实例对象if(mbd。isSingleton()){使用一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象sharedInstancegetSingleton(beanName,(){try{returncreateBean(beanName,mbd,args);}catch(BeansExceptionex){Explicitlyremoveinstancefromsingletoncache:Itmighthavebeenputthereeagerlybythecreationprocess,toallowforcircularreferenceresolution。Alsoremoveanybeansthatreceivedatemporaryreferencetothebean。显式地从容器销毁给定的beandestroySingleton(beanName);throwex;}});sharedInstance可能是一个FactoryBean,如果是FactoryBean那么真正需要拿到的是getObject方法所返回的对象beangetObjectForBeanInstance(sharedInstance,name,beanName,mbd);}}复制代码
  关注一下getSingleton(StringbeanName,ObjectFactorylt;?singletonFactory)方法,该方法与第零步一模一样。publicObjectgetSingleton(StringbeanName,ObjectFactorylt;?singletonFactory){Assert。notNull(beanName,Beannamemustnotbenull);synchronized(this。singletonObjects){去单例池中获取实例ObjectsingletonObjectthis。singletonObjects。get(beanName);如果不存在实例,则创建单例bean实例if(singletonObjectnull){忽略把当前正在创建的beanName添加到singletonsCurrentlyInCreation中,singletonsCurrentlyInCreation是一个Set表示这些bean正常创建中,在没创建完时不能重复创建beforeSingletonCreation(beanName);booleannewSingletonfalse;booleanrecordSuppressedExceptions(this。suppressedExceptionsnull);if(recordSuppressedExceptions){this。suppressedExceptionsnewLinkedHashSet();}try{singletonFactory是外面传进来的lambda表达式,执行lambda表达式就是调用createBean()singletonObjectsingletonFactory。getObject();newSingletontrue;}忽略将创建好的单例bean添加到单例池singletonObjects中和单例注册表registeredSingletons中并清除二级、三级缓存if(newSingleton){addSingleton(beanName,singletonObject);}}returnsingletonObject;}}复制代码
  这里就会走B的生命周期了。进行实例化。singletonFactory是外面传进来的lambda表达式,执行lambda表达式就是调用createBean()singletonObjectsingletonFactory。getObject();复制代码
  进行完B的实例化之后,然后进行填充属性。发现需要填充A属性,再走A的getBean方法。
  既然是走getBean的方法,那就会进入到第二步所分析的getSingleton方法。NullableprotectedObjectgetSingleton(StringbeanName,booleanallowEarlyReference){从单例池中获取实例ObjectsingletonObjectthis。singletonObjects。get(beanName);如果从单例池中没有获取到实例并且指定的单例bean正在创建中if(singletonObjectnullisSingletonCurrentlyInCreation(beanName)){锁定全局变量并进行处理synchronized(this。singletonObjects){earlySingletonObjects二级缓存singletonObjectthis。earlySingletonObjects。get(beanName);没有从二级缓存中获取到并且allowEarlyReference为trueif(singletonObjectnullallowEarlyReference){实例化之后调用addSingletonFactory方法将ObjectFactory初始化存储在singletonFactoriesMap中singletonFactories三级缓存ObjectFactorylt;?singletonFactorythis。singletonFactories。get(beanName);if(singletonFactory!null){执行lambdaAOpsingletonObjectsingletonFactory。getObject();记录在缓存中earlySingletonObjects与singletonFactories互斥this。earlySingletonObjects。put(beanName,singletonObject);this。singletonFactories。remove(beanName);}}}}returnsingletonObject;}复制代码
  从单例池获取aBean那是也是获取不到的,所以singletonObject值为null。至于a是否正在创建中,a现在是在创建中了,所以进入if代码块,从二级缓存中获取实例,肯定获取不到,所以为null,至于allowEarlyReference的值,由上层传入true。然后从三级缓存根据beanName(也就是a)获取到ObjectFactory类型的一个函数式接口。这时候是有值的,我们在A实例化之后,就将key为a的ObjectFactory接口放入到了三级缓存中了。然后调用singletonFactory的getObject(),也就是执行lambda体。我们现在可以知道lambda体是在产生循环依赖的时候才会被执行。然后将获得的对象保存在二级缓存中,将三级缓存的记录进行删除。
  lambda体执行完毕之后,会返回一个对象。那该对象一定是代理对象吗?
  执行lambda体,也技术执行getEarlyBeanReference方法。进入AbstractAutowireCapableBeanFactory类getEarlyBeanReference方法,protectedObjectgetEarlyBeanReference(StringbeanName,RootBeanDefinitionmbd,Objectbean){ObjectexposedObjectbean;if(!mbd。isSynthetic()hasInstantiationAwareBeanPostProcessors()){for(BeanPostProcessorbp:getBeanPostProcessors()){AOPif(bpinstanceofSmartInstantiationAwareBeanPostProcessor){SmartInstantiationAwareBeanPostProcessoribp(SmartInstantiationAwareBeanPostProcessor)bp;exposedObjectibp。getEarlyBeanReference(exposedObject,beanName);}}}returnexposedObject;}复制代码
  先将原始对象赋予给另外一个变量。获得所有的BeanPostProcessor,判断是否是SmartInstantiationAwareBeanPostProcessor,如果是则执行getEarlyBeanReference方法。将原始对象和bean名称作为入参传入。
  调试进入SmartInstantiationAwareBeanPostProcessor类,可以发现getEarlyBeanReference方法下有两个子类实现。一个是InstantiationAwareBeanPostProcessorAdapter,另外一个是AbstractAutoProxyCreator。这里我们看AbstractAutoProxyCreator就好了,因为InstantiationAwareBeanPostProcessorAdapter没有进行具体的实现。
  进入AbstractAutoProxyCreator类,其实这时候我们可以发现该类所处的包路径为
  org。springframework。aop。framework。autoproxy;,可以看到一个眼熟的词aop。获取提前暴露的bean的引用,用来支持单例对象的循环引用,解决循环依赖问题OverridepublicObjectgetEarlyBeanReference(Objectbean,StringbeanName){ObjectcacheKeygetCacheKey(bean。getClass(),beanName);this。earlyProxyReferences。put(cacheKey,bean);returnwrapIfNecessary(bean,beanName,cacheKey);可能会产生代理对象}复制代码
  该方法里有三行代码,我们一行一行的看。getCacheKeyprotectedObjectgetCacheKey(Classlt;?beanClass,NullableStringbeanName){if(StringUtils。hasLength(beanName)){isAssignableFrom:判断当前的Class所表示的类,是不是参数中传递的Class所表示的类的父类,超接口,或者是相同的类型是则返回true,否则返回false使用方式:父类。class。isAssignableFrom(子类。class)return(FactoryBean。class。isAssignableFrom(beanClass)?BeanFactory。FACTORYBEANPREFIXbeanName:beanName);}else{returnbeanClass;}}复制代码
  首先判断当前beanName不等于null并且不等于空字符串,然后使用三元表达式计算当前beanClass是否是FactoryBean子类或者子实现,如果是则将beanName拼接上。
  第二行往earlyProxyReferencesMap容器里添加了一条记录,key为cacheKey的值,value为原始对象。在生命周期初始化后那步就会根据earlyProxyReferencesMap进行判断,是否进行过AOP。
  接下来我们看最重要的一个方法:wrapIfNecessary,方法名翻译之后大意是如果需要包装,也就是说,如果要包装则产生代理对象,否则返回原对象。是否要产生代理对象是有条件的。wrapIfNecessaryprotectedObjectwrapIfNecessary(Objectbean,StringbeanName,ObjectcacheKey){在当前targetSourcedBeans中存在的bean,表示在实例化之前就产生了代理对象直接返回,那就不要再次产生代理对象了if(StringUtils。hasLength(beanName)this。targetSourcedBeans。contains(beanName)){returnbean;}advisedBeans是一个map,存储的是class:是否应该被代理,为true则需要被代理当前这个bean不用被代理if(Boolean。FALSE。equals(this。advisedBeans。get(cacheKey))){returnbean;}如果是普通bean,或者继承Advisor、Pointcut、AdviceAopInfrastructureBean的类不需要被代理if(isInfrastructureClass(bean。getClass())shouldSkip(bean。getClass(),beanName)){将当前cacheKey标记为不用被代理this。advisedBeans。put(cacheKey,Boolean。FALSE);returnbean;}获取当前beanClass所匹配的advisorsObject〔〕specificInterceptorsgetAdvicesAndAdvisorsForBean(bean。getClass(),beanName,null);如果匹配的advisors不等于null,那么则进行代理,并返回代理对象if(specificInterceptors!DONOTPROXY){将当前cacheKey标记为需要被代理this。advisedBeans。put(cacheKey,Boolean。TRUE);基于bean对象和Advisor创建代理对象ObjectproxycreateProxy(bean。getClass(),beanName,specificInterceptors,newSingletonTargetSource(bean));存一个代理对象的类型this。proxyTypes。put(cacheKey,proxy。getClass());returnproxy;}this。advisedBeans。put(cacheKey,Boolean。FALSE);returnbean;}复制代码
  上面AOP的逻辑走完了之后,就会将A对象进行返回,然后赋值给B。B的属性填充也就此完成,那就会继续往下走。if(earlySingletonExposure){earlySingletonReference只有在检测到有循环依赖的情况下才会不为空ObjectearlySingletonReferencegetSingleton(beanName,false);if(earlySingletonReference!null){如果提前暴露的对象(bean)和经过了完整的生命周期后的对象相等(exposedObject)则把缓存中的earlySingletonReference赋值给exposedObject最终会添加到singletonObjects中去(初始化之后的bean等于原始的bean,说明不是proxy),if(exposedObjectbean){exposedObjectearlySingletonReference;}检测该bean的dependon的bean是否都已经初始化好了elseif(!this。allowRawInjectionDespiteWrappinghasDependentBean(beanName)){String〔〕dependentBeansgetDependentBeans(beanName);SetStringactualDependentBeansnewLinkedHashSet(dependentBeans。length);for(StringdependentBean:dependentBeans){if(!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)){actualDependentBeans。add(dependentBean);}}因为bean创建后;其所依赖的bean一定是创建了的。actualDependentBeans不为空表示当bean创建后依赖的bean没有全部创建完,也就是说存在循环依赖if(!actualDependentBeans。isEmpty()){thrownewBeanCurrentlyInCreationException(beanName,BeanwithnamebeanNamehasbeeninjectedintootherbeans〔StringUtils。collectionToCommaDelimitedString(actualDependentBeans)〕initsrawversionaspartofacircularreference,buthaseventuallybeenwrapped。Thismeansthatsaidotherbeansdonotusethefinalversionofthebean。ThisisoftentheresultofovereagertypematchingconsiderusinggetBeanNamesOfTypewiththeallowEagerInitflagturnedoff,forexample。);}}}}try{8、注册DisposableBeanregisterDisposableBeanIfNecessary(beanName,bean,mbd);}catch(BeanDefinitionValidationExceptionex){thrownewBeanCreationException(mbd。getResourceDescription(),beanName,Invaliddestructionsignature,ex);}returnexposedObject;复制代码getSingleton:注意allowEarlyReference的值为false。NullableprotectedObjectgetSingleton(StringbeanName,booleanallowEarlyReference){从单例池中获取实例ObjectsingletonObjectthis。singletonObjects。get(beanName);如果从单例池中没有获取到实例并且指定的单例bean正在创建中if(singletonObjectnullisSingletonCurrentlyInCreation(beanName)){锁定全局变量并进行处理synchronized(this。singletonObjects){earlySingletonObjects二级缓存singletonObjectthis。earlySingletonObjects。get(beanName);没有从二级缓存中获取到并且allowEarlyReference为trueif(singletonObjectnullallowEarlyReference){实例化之后调用addSingletonFactory方法将ObjectFactory初始化存储在singletonFactoriesMap中singletonFactories三级缓存ObjectFactorylt;?singletonFactorythis。singletonFactories。get(beanName);if(singletonFactory!null){执行lambdaAOpsingletonObjectsingletonFactory。getObject();记录在缓存中earlySingletonObjects与singletonFactories互斥this。earlySingletonObjects。put(beanName,singletonObject);this。singletonFactories。remove(beanName);}}}}returnsingletonObject;}复制代码
  又是这个方法,带大家再来看看这个方法。这个时候beanName还是为b,B的生命周期还没走完呢。从单例池获取》没有,b还在创建中,所以进入if,然后从二级里面获取》没有,allowEarlyReference由上层传入false,结束方法,返回null。返回null,就不会符合earlySingletonReference!null的判断,直接调用registerDisposableBeanIfNecessary方法,进行Bean销毁逻辑。protectedvoidregisterDisposableBeanIfNecessary(StringbeanName,Objectbean,RootBeanDefinitionmbd){AccessControlContextacc(System。getSecurityManager()!null?getAccessControlContext():null);if(!mbd。isPrototype()requiresDestruction(bean,mbd)){if(mbd。isSingleton()){RegisteraDisposableBeanimplementationthatperformsalldestructionworkforthegivenbean:DestructionAwareBeanPostProcessors,DisposableBeaninterface,customdestroymethod。registerDisposableBean(beanName,newDisposableBeanAdapter(bean,beanName,mbd,getBeanPostProcessors(),acc));}else{Abeanwithacustomscope。。。Scopescopethis。scopes。get(mbd。getScope());if(scopenull){thrownewIllegalStateException(NoScoperegisteredforscopenamembd。getScope());}scope。registerDestructionCallback(beanName,newDisposableBeanAdapter(bean,beanName,mbd,getBeanPostProcessors(),acc));}}}复制代码
  只要看requiresDestruction方法就好了。protectedbooleanrequiresDestruction(Objectbean,RootBeanDefinitionmbd){判断某个Bean是否拥有销毁方法1。实现了DisposableBean接口或AutoCloseable接口2。BeanDefinition中定义了destroyMethodName3。类中是否存在PreDestroy注解的方法return(bean。getClass()!NullBean。class(DisposableBeanAdapter。hasDestroyMethod(bean,mbd)(hasDestructionAwareBeanPostProcessors()DisposableBeanAdapter。hasApplicableProcessors(bean,getBeanPostProcessors()))));}

为什么很多女生都喜欢陈冠希?为什么陈冠希会有那么多女生喜欢?看看陈老师的这些就知道了。随着某照门的事件过去这么多年,陈冠希已经退出了娱乐圈,但是陈老师人不在江湖,江湖却一直有他的传说。那么陈冠希为什么会被那么都说棕色显老,但很喜欢棕色系的搭配,怎么搭配棕色系才好看呢?相信大多数人喜欢比较亮丽鲜艳的颜色,给我们带来的视觉感受是心旷神怡的,而且还能让我们的心情也会好上许多,颜色的使用堪称全领域覆盖,特别是时尚领域更是离不开它。商场里和女生衣橱里那些有哪些电视剧一定要趁年轻看?幼老勿进,这些美剧一定要趁年轻看!第一部名姝趁年轻看原因年轻不看,老了容易犯错误这部剧主要讲述了1763年伦敦的地下产业。为了生存,大部分女性出卖自己的尊严,来换取可怜的生活用品。您见过放心脏支架最年轻的患者有多大?我见过最年轻的,一个25岁一个26岁,他们同样年轻,但是结果却完全不同。一个差点死掉一个放了四个支架。25岁那个小伙子,是因为急性胸痛到急诊就诊的,急诊做了心电图,发现是心肌梗死,B型房车和C型房车哪一款最适合老年人自驾游?我来回答您的问题两人出行建议选B型房车,驾驶灵活安全低调。笼式车身,房车厂改造部分仅限于车厢内部,车身框架都是原厂出品,保证了车辆整体性比较安全。通过性好比较灵巧,B型房车尽量选自家用打印机,给小孩子打作业,用哪种好?首先声明,我没有网店,也不会卖给大家打印机,我只能给个大致的参考,如果您信了我的推荐,结果踩了坑,我也没法负责,毕竟每个人的动手能力,对性价比的理解都不一样。或许是从疫情来临,在家指甲月牙代表什么?月牙的颜色奶白色为好,越白越好,表示精力越壮。不正常的月牙颜色灰色表示精弱,影响脾胃消化吸收功能的运行,容易引起贫血,疲倦乏力。黑色多见于严重的心脏病肿瘤或长期服药引起药物和重金属糖尿病人可食用燕麦片吗?谢谢邀请。糖尿病人可以吃燕麦片。严格来说,没有什么食物是糖尿病人绝对不能吃的,关键是吃多少以及怎么吃。燕麦片为什么适合糖尿病人吃?燕麦含有丰富的蛋白质膳食纤维矿物质和维生素,尤其富教师资格证面试时考官问自己叫什么,能不能直接报出自己姓名,为什么?一般来说,教师资格证的筛选是有一个机制的。比如说职称一般是在一级以上。也就是说这些面试官是有一定的工作经验的。在担任教师资格证考试面试官前,他们还要经过教育管理机构的培训,然后才会安徽省阜阳市临泉第一中学怎么样?谈及自己的母校,心中还是很自豪的。现在临泉一中已经有了新校区,我是从老校区毕业的。临泉一中,毫无疑问,临泉最好的高中,在阜阳也当属一流,其实力不亚于阜阳一中。本科达线率高达90以上112所211高校而双一流只有96所哪几个没有进入双一流?在回答题主问题前,有几个高等教育重要工程概念,我需要先给大家说明白211工程啥意思1995年,我国面向二十一世纪,重点建设一百所左右的高校和重点学科的高等教育伟大世纪工程,截止目前
细数斯诺克大师赛的五大纪录斯诺克大师赛,传统三大赛之一。历史地位极高,仅次于世锦赛,和英锦赛地位相同,历来奖金颇高。斯诺克大师赛历史上最成功的球员是火箭奥沙利文,有奥沙利文的比赛就有票房的绝对保障。斯诺克大为世界杯呐喊!南京苏宁易购ampampamp海信强强联合!南京苏宁易购海信璀璨杯足球友谊赛开幕,大屏视界杯营销全面引爆为世界杯呐喊!南京苏宁易购海信强强联合!近日,在南京白马中心球场,苏宁易购南京大区和海信南京营销中心共同开启了苏宁易购海同曦击败天津,四川战胜福建,第十九轮过后,积分榜排名情况如何悄然间,联赛第十九轮比赛已经落下帷幕。在此前结束的比赛中,同曦战胜天津,四川击败福建。第十六轮比赛全部结束后,联赛积分榜排名情况如何呢?浙粤辽稳居前三第十九轮比赛中,浙江广东双双赢从罗马弃将到皇马后防核心,全能战士勤奋且低调,却被队友们拖累提起在二十一世纪担任过皇马后防核心的球员,大家会想起哪些名字?相信绝大多数人的第一反应,无非就是耶罗拉莫斯这些球迷们耳熟能详的存在。至于埃尔格拉这位昔日的皇马后防核心,只怕就连不少京津两队提交末轮弃赛申请武汉三镇提前获得中超冠军12月29日,北京青年报记者获悉,受客观因素影响,天津津门虎俱乐部北京国安俱乐部已分别就放弃参加中超联赛第34轮,也就是最后一轮比赛,向赛事主办方提交了申请。这意味着一旦申请获得通北京茶桌怪象,极少见碧螺春龙井,他们却偏爱这3款平价茶国内众多城市中,最让人向往的就是北上广深,因为这几个城市比较繁华,也是我国的经济中心。这几个城市给人的感觉一直是非常有钱,所以很多人就觉得这些地方的人,都是奢侈品加身,但其实并非如蒙脱石散一夜脱销!概念股要嗨?这两股已登顶人气榜2023年伊始又迎来一波抢药潮,这次是止泻药蒙脱石散。近日,一张XBB1。5毒株在美国登顶,此毒株主攻心脑血管和肚子,建议准备蒙脱石散的截图在市场上迅速流传,随后蒙脱石散药物开始遭国内已发现新毒株!二次感染风险增强?最新回应!陕西已开始实施!上海已检测到XBB毒株近日,奥密克戎亚型毒株XBB。1。5毒株,引发网友关注,相关话题冲上热搜。瑞金医院陈赛娟和公卫中心范小红领衔的联合科研攻关团队经过初步分析后公布的上海近期30儿童面条更有营养?实测发现坑不少,有些根本不适合给娃吃贴着营养丰富成分更安全儿童专属等标签售出高价,实际成分却和普通成人款相差无几。这种情况,在儿童食品中很常见。今天,老爸评测来说说家长高频购买,但很容易买错的辅食儿童面条!给孩子吃的这些年流行的学习伪概念,量子速读,右脑开发真假不清的伪概念很多人听过左右脑分工,左脑逻辑,语言,分析,推理右脑图像,空间,音乐,韵律。这个所谓的常识,来源于很久之前的一个研究结论。最初是斯佩里研究癫痫病人,把他们左右脑之间具俊晔公开洗护过程,每早都要全头剃毛,疑因吸毒不敢留头发近日,具俊晔的洗护过程被公开之后引起了大家的关注。从网上的图片中可以了解到,每天早上他都要做全套的护理,耗费的时间甚至比大S还要长。在起床之后他就会开始梳洗打扮,相对于普通男性来说
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网