spring源码关于循环引用的笔记,spring解决循环依赖原理

  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这一属性,当发现依赖后,会调用DefaultListableBeanFactorydoResolveDependency方法,之后执行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方法,尝试使用beanFactorygetBean去获取serviceA

  

  向下执行,调用getSingleton方法尝试直接获取serviceA,此时三级缓存singletonFactories中我们之前已经存进去了一个key为serviceAbeanName,value为lambda表达式,这时可以直接获取到。

  

  在执行singletonFactorygetObject方法时才去真正执行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的方法,可以看到serviceBserviceA属性已经被注入了,但是serviceA中的serviceB属性还是null。说明serviceB的依赖注入已经完成,而serviceA的依赖注入还没做完。

  

  现在我们梳理一下运行到这里的流程:

  1、在serviceA填充属性过程中发现依赖了serviceB,通过beanFactorygetBean方法,尝试获取serviceB

  2、serviceB不存在,执行了一遍serviceB的创建流程,填充属性时发现serviceA已经存在于三级缓存,直接注入给serviceB

  可以看到,在创建serviceA的过程中发现依赖的serviceB不存在,转而去创建了serviceB,而创建serviceA的流程并没有执行完,因此在创建完serviceB后再顺调用链返回,直到doResolveDependency方法:

  

  可以看到,需要依赖的serviceB已经被创建并返回成功,返回到inject方法,同样通过反射给serviceB赋值:

  

  返回doCreateBean方法,可以看到serviceAserviceB之间的循环依赖已经完成了:

  

  这样,一个最简单的循环依赖流程就结束了。有的小伙伴可能会提出疑问,这样的话,我只需要添加一个缓存存放原生对象就够了啊,为什么还需要二级缓存和三级缓存两层结构呢?这个问题,我们放在下一篇具体讨论,看看循环依赖的具体实现时怎样的。

  到此这篇关于Spring源码解析之循环依赖的实现流程的文章就介绍到这了,更多相关Spring循环依赖内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • spring编程式事务处理,spring编程事务
  • spring编程式事务处理,spring编程事务,详解Spring学习之编程式事务管理
  • spring的核心功能模块有几个,列举一些重要的spring模块
  • spring的核心功能模块有几个,列举一些重要的spring模块,七个Spring核心模块详解
  • spring注解和springmvc的注解,SpringMVC常用注解
  • spring注解和springmvc的注解,SpringMVC常用注解,详解springmvc常用5种注解
  • spring实现ioc的四种方法,spring的ioc的三种实现方式
  • spring实现ioc的四种方法,spring的ioc的三种实现方式,简单实现Spring的IOC原理详解
  • spring事务失效问题分析及解决方案怎么做,spring 事务失效情况
  • spring事务失效问题分析及解决方案怎么做,spring 事务失效情况,Spring事务失效问题分析及解决方案
  • spring5.0新特性,spring4新特性
  • spring5.0新特性,spring4新特性,spring5新特性全面介绍
  • spring ioc以及aop原理,springmvc aop原理
  • spring ioc以及aop原理,springmvc aop原理,深入浅析Spring 的aop实现原理
  • Spring cloud网关,spring cloud zuul作用
  • 留言与评论(共有 条评论)
       
    验证码: