3.4 spring5源码系列--循环依赖的设计思想 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖3.2spring源码系列----循环依赖源码分析3.3 Spring5源码---循环依赖过程中spring读取不完整bean的最终

前端之家收集整理的这篇文章主要介绍了3.4 spring5源码系列--循环依赖的设计思想 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖3.2spring源码系列----循环依赖源码分析3.3 Spring5源码---循环依赖过程中spring读取不完整bean的最终前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

前面已经写了关于三篇循环依赖的文章,这是一个总结篇

第一篇: 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖

第二篇: 3.2spring源码系列----循环依赖源码分析

第三篇: 3.3 Spring5源码---循环依赖过程中spring读取不完整bean的最终

现在总结循环依赖的思想

学了那么多,为什么说见多才能识广呢,知道别人是如何解决某一类问题的,也就是优秀代码的魅力. 这也是为什么要学习别人的代码的原因.

思想才是我们可以在工作中借鉴使用的

1. 循环依赖的三级缓存设计

2. 接口函数

 


 

一. 循环依赖的三级缓存设计

再循环依赖的过程中设计了三级缓存,他们的作用分别是

1. 一级缓存: 用来存放完整的bean

2. 二级缓存: 用来存放早期的,纯净的bean

3. 三级缓存: 用来存放接口函数.

  1.    /** Cache of singleton objects: bean name to bean instance. */
  2. /**
  3. * 一级缓存 这个就是我们大名鼎鼎的缓存池,用于保存我们所有的实例bean
  4. private final Map<String,Object> singletonObjects = new ConcurrentHashMap<>(256);
  5. * Cache of singleton factories: bean name to ObjectFactory. *
  6. * 三级缓存 该map用户缓存key为beanName,value为objectFactory(包装为早期对象)
  7. singletonFactories = new HashMap<>(16* Cache of early singleton objects: bean name to bean instance. *
  8. * 二级缓存,用户缓存我们的key为beanName,value是我们的早期对象(此时对象属性还没有...)
  9. earlySingletonObjects = 16);

 

细细想来,这三个缓存都非常有存在的必要.

1.1 引入一级缓存

刚开始,只有一级缓存,在整个bean创建完成以后,将其完整的bean放入到一级缓存中. 这样有什么问题? 

1. bean创建一共有三大步骤,(实例化,属性赋值,初始化) 等到整个过程都创建完,在存入一级缓存,多线程怎么办? 第一个线程创建bean的过程中,又来了一个线程,他发现一级缓存这时候还没有,就回去再次创建. 那不就重复了么? ioc要求,bean是单例的.

2. 加锁,加锁能否解决这个问题? 能,但是效率超级低. 对一级缓存加锁,那么所有的对象创建过程中都要等待. 哪怕人家已经创建成功过. 效率太低,不能接受

3. 于是就引入了二级缓存. 

1.2 引入二级缓存

二级缓存的引入,可以解决一级缓存创建bean链路过长的问题,他在bean一旦被创建,立刻就放入到二级缓存. 整个bean创建完成以后,在放入到一级缓存,删除二级缓存. 这样做可以解决多线程创建bean的问题. 缩短了整个链路. 同时,每次从缓存中先获取bean,如果一级缓存中已经有了,那么直接返回. 不用在执行后面的创建代码

那么,二级缓存有什么问题呢?

这就还需要知道一个问题,那就是动态代理创建bean. 什么时候,去使用动态代理创建bean? 通常我们说在初始化之后,调用bean的后置处理器创建bean. 这只是大多数bean创建动态代理的时候. 那如果有循环依赖呢? 有循环依赖,还在初始化之后创建就晚了. 这是需要在实例化之后创建. 这样,动态代理的代码就和创建bean耦合在一块了. 违背单一性原则.

于是,引入了三级缓存

1.3 引入三级缓存

三级缓存的引入是为了解决耦合问题. 让每一个方法只做一件事. 巧妙的使用了接口函数

 这个接口函数什么用呢? 就相当于js中的回调函数. 我在前面定义好,但是不执行. 直到满足条件了,才执行. 这个方法,可以大范围应用到实践工作中.

比如: 调用动态代理创建bean. 刚开始实例化完成以后,我就赋予你这个能力,你可以调用动态代理. 但是,到后面,你是否真的能够运用这个能力呢? 不一定,只有满足条件,才会运用这个能力. 

二. 定义接口函数,也叫钩子函数

在循环依赖源码中,两次使用到接口函数的方式. 

第一个是创建bean的时候. 第二个是三级缓存

下面来看看源码,

第一次: 创建bean的时候,定义了一个钩子函数createBean()

  1. sharedInstance = getSingleton(beanName,() -> {
  2. try {
  3. // 这里定义了一个钩子函数. 此时只是定义,并不执行. 在真正需要创建bean的地方才会执行
  4. return createBean(beanName,mbd,args);
  5. }
  6. catch (BeansException ex) {
  7. Explicitly remove instance from singleton cache: It might have been put there
  8. eagerly by the creation process,to allow for circular reference resolution.
  9. Also remove any beans that received a temporary reference to the bean.
  10. destroySingleton(beanName);
  11. throw ex;
  12. }
  13. });

实际上调用的时机是: 在getSingleton方法里面. 回调接口函数.

  1. public Object getSingleton(String beanName,ObjectFactory<?> singletonFactory) {
  2. Assert.notNull(beanName,"Bean name must not be null");
  3. synchronized (this.singletonObjects) {
  4. 第一步: 从一级缓存中获取单例对象
  5. Object singletonObject = this.singletonObjects.get(beanName);
  6. if (singletonObject == null) {
  7. if (.singletonsCurrentlyInDestruction) {
  8. throw new BeanCreationNotAllowedException(beanName,1)">Singleton bean creation not allowed while singletons of this factory are in destruction " +
  9. (Do not request a bean from a beanfactory in a destroy method implementation!));
  10. }
  11. if (logger.isDebugEnabled()) {
  12. logger.debug(Creating shared instance of singleton bean '" + beanName + ' 第二步: bean添加singletonsCurrentlyInCreation中,表示bean正在创建
  13. beforeSingletonCreation(beanName);
  14. boolean newSingleton = false;
  15. boolean recordSuppressedExceptions = (this.suppressedExceptions == );
  16. (recordSuppressedExceptions) {
  17. this.suppressedExceptions = new LinkedHashSet<>();
  18. }
  19. {
  20. // 第三步: 这里调用getObject()钩子方法,就会回调匿名函数,调用singletonFactory的createBean()
  21. singletonObject = singletonFactory.getObject();
  22. newSingleton = true;
  23. }
  24. (IllegalStateException ex) {
  25. Has the singleton object implicitly appeared in the meantime ->
  26. if yes,proceed with it since the exception indicates that state.
  27. singletonObject = (beanName);
  28. ) {
  29. throw ex;
  30. }
  31. }
  32. (BeanCreationException ex) {
  33. (recordSuppressedExceptions) {
  34. for (Exception suppressedException : .suppressedExceptions) {
  35. ex.addRelatedCause(suppressedException);
  36. }
  37. }
  38. ex;
  39. }
  40. finally {
  41. ;
  42. }
  43. afterSingletonCreation(beanName);
  44. }
  45. (newSingleton) {
  46. addSingleton(beanName,singletonObject);
  47. }
  48. }
  49. singletonObject;
  50. }
  51. }

 

第二次调用: 是在三级缓存定义的时候

调用addSingletonFactory(...)定义了一个钩子函数. 这里仅仅是定义,并不执行

  1. 把我们的早期对象包装成一个singletonFactory对象,该对象提供了getObject()方法,把静态的bean放到三级缓存中去了.
  2. addSingletonFactory(beanName,() -> getEarlyBeanReference(beanName,bean));

然后进入到addSingletonFactory内部,只是把singletonFactory放入到了三级缓存中,这里只是定义,也并没有执行

  1. protected void addSingletonFactory(String beanName,ObjectFactory<?> singletonFactory) {
  2. Assert.notNull(singletonFactory,1)">Singleton factory must not be nullif (!.singletonObjects.containsKey(beanName)) {
  3. 加入到三级缓存中,暴露早期对象用于解决循环依赖.
  4. this.singletonFactories.put(beanName,singletonFactory);
  5. 从二级缓存中删除
  6. .earlySingletonObjects.remove(beanName);
  7. 添加到已经注册的singleton实例.
  8. .registeredSingletons.add(beanName);
  9. }
  10. }
  11. }

什么时候执行的呢? 再从缓存中获取对象的时候. 

  1. @Nullable
  2. protected Object 从一级缓存中获取bean实例对象
  3. Object singletonObject = (beanName);
  4. *
  5. * 如果在第一级的缓存中没有获取到对象,并且singletonsCurrentlyIncreationtrue,也就是这个类正在创建.
  6. * 标明当前是一个循环依赖.
  7. *
  8. * 这里有处理循环依赖的问题.-- 我们使用三级缓存解决循环依赖
  9. */
  10. null && isSingletonCurrentlyInCreation(beanName)) {
  11. synchronized (.singletonObjects) {
  12. *
  13. * 从二级缓存中拿bean,二级缓存中的对象是一个早期对象
  14. * 什么是早期对象?就是bean刚刚调用了构造方法,还没有给bean属性进行赋值,和初始化,这就是早期对象
  15. */
  16. singletonObject = this.earlySingletonObjects.(beanName);
  17. allowEarlyReference) {
  18. *
  19. * 从三级缓存拿bean,singletonFactories是用来解决循环依赖的关键所在.
  20. * ios后期的过程中,当bean调用了构造方法的时候,把早期对象包装成一个ObjectFactory对象,暴露在三级缓存中
  21. */
  22. ObjectFactory<?> singletonFactory = this.singletonFactories.(beanName);
  23. if (singletonFactory != ) {
  24. /**
  25. * 在这里通过暴露的ObjectFactory包装对象. 通过调用他的getObject()方法获取对象
  26. * 在这个环节中会调用getEarlyBeanReference()来进行后置处理
  27. */
  28. singletonObject = singletonFactory.getObject();
  29. 把早期对象放置在二级缓存中
  30. .earlySingletonObjects.put(beanName,singletonObject);
  31. 删除三级缓存
  32. .singletonFactories.remove(beanName);
  33. }
  34. }
  35. }
  36. }
  37. singletonObject;
  38. }

在这里调用三级缓存, singletonObject = singletonFactory.getObject(); 回调钩子函数

猜你在找的Spring相关文章