zoukankan      html  css  js  c++  java
  • spring源码学习(三)spring循环引用源码学习 小小少年

    在spring中,是支持单实例bean的循环引用(循环依赖)的,循环依赖,简单而言,就是A类中注入了B类,B类中注入了A类,首先贴出我的代码示例

     1 @Component
     2 public class AddressVO {
     3 
     4     @Autowired
     5     private UserVO userVO;
     6 }
     7 
     8 
     9 @Component
    10 public class UserVO {
    11 
    12     @Autowired
    13     private AddressVO addressVO;
    14 
    15     public void test(){
    16         System.out.println(addressVO.getClass());
    17     }
    18 }
    19 
    20 @Configuration
    21 @ComponentScan("com.springsource.study.reference")
    22 public class AppConfig {
    23 }

    前提是,bean都是单实例的(singleton),我们下面在学习源码的时候,也默认当前bean是单实例的;

    1.首先说,循环依赖的处理是在refresh()方法中,finishBeanFactoryInitialization(beanFactory);这个方法中完成的;这个方法的作用是:在spring将所有的bean添加到beanDefinitionMap之后,实例化所有的bean;

     先说明spring在解决循环依赖所用到的几个比较重要的collection

     1   //在实例化bean的时候,如果bean正在被创建,会在beforeSingletonCreation()方法中,将当前bean加入到singletonsCurrentlyInCreation 2   private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16));
     3  4 
     5   //从狭义上来理解,spring容器就是signletonObjects这个map;但是,详细点来讲,就是由beanDefinition+beanDefinitionMaps+beanFactory+beanFactoryPostprocessor+singletonObjects....等一些列组件的集合
     6   //这是spring单实例池(也就是我们常说的spring容器)
     7   private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
     8 
     9   //我们姑且称为spring的二级缓存,addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));在该方法中,会将bean包装为ObjectFactory,存放到map中
    10   private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
    11 
    12   //暂时称为三级缓存 三级缓存在循坏依赖中,用来防止重复创建
    13   private final Map<String, Object> earlySingletonObjects = new HashMap(16);

    上面这几个集合,在源码中会一一解析

    上面这里少说了一个变量:allowCircularReferences;这个值默认是true;表示是否允许循环依赖

    2.我们直接来说源码吧:

      org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons

        org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)
          org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

    在doGetBean()方法中,我们首先要说的是Object sharedInstance = getSingleton(beanName);这个方法:

     1 @Nullable
     2 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
     3     Object singletonObject = this.singletonObjects.get(beanName);
     4     if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
     5         synchronized (this.singletonObjects) {
     6             //这里只需要从二级缓存中拿一次就行,如果没有二级缓存,每次进来都需要从二级缓存get一次,影响效率
     7             singletonObject = this.earlySingletonObjects.get(beanName);
     8             if (singletonObject == null && allowEarlyReference) {
     9                 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    10                 if (singletonFactory != null) {
    11                     singletonObject = singletonFactory.getObject();
    12                     this.earlySingletonObjects.put(beanName, singletonObject);
    13                     this.singletonFactories.remove(beanName);
    14                 }
    15             }
    16         }
    17     }
    18     return singletonObject;
    19 }

    在第一次初始化bean,走到这个方法的时候,这里返回的肯定是null,因为在第一次创建的时候,单实例池、二级缓存、三级缓存中都是null;

    这里先这样理解,在后面还会介绍这个方法;在这里返回null之后,spring就会继续往下进行创建初始化的流程;

    org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

    在doGetBean方法中的,有这样一段代码

     1 if (mbd.isSingleton()) {
     2     sharedInstance = getSingleton(beanName, () -> {
     3         try {
     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     });
    14     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    15 }

    这里判断到当前bean是单实例的时候,在getSingleton方法中有这么一行代码:beforeSingletonCreation(beanName);

    这个方法,就是把当前bean添加到了singletonsCurrentlyInCreation这个set集合中;这个set的作用,在后面会用到;

    接下来,会继续执行初始化的代码,中间的创建流程不做介绍了哈,直接说关键点

    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

    在这个方法中,有这么几行重要的代码

     1 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
     2         isSingletonCurrentlyInCreation(beanName));
     3 if (earlySingletonExposure) {
     4     if (logger.isDebugEnabled()) {
     5         logger.debug("Eagerly caching bean '" + beanName +
     6                 "' to allow for resolving potential circular references");
     7     }
     8     //第四次调用后置处理器  用来解决循环依赖
     9     addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    10 }

    这段代码的意思是:

      如果当前bean是单实例的,并且允许循环依赖;isSingletonCurrentlyInCreation这个方法,就是判断当前bean是否在singletonsCurrentlyInCreation这个set中,也就是说判断当前bean是否正在被创建

      这三个条件都满足,所以会调用后置处理器

    关键代码是这个this.singletonFactories.put(beanName, singletonFactory); 将后置处理器返回的singletonFactory放到了我们所说的二级缓存中

    再继续执行流程,初始化bean之后,会在populateBean(beanName, mbd, instanceWrapper);这个方法中,进行属性的注入;

    关键点就在这里:

      我们就以A类和B类来说了

        2.1 首先要明白的是,上面这一整个流程是第一个A这个bean初始化的过程;A类在进行属性注入的时候,会发现依赖了B类,这时候,会调用 org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String);

        2.2 因为这时候,B类还没有初始化,所以,会进行B类的创建(B类的创建,就是再走一步我们上面说的流程,),由于流程是一模一样的,添加到set集合中,调用后置处理器往二级缓存中放入singletonFactory,这些都一样,当B类来到属性注入的时候;会发现B类依赖了A类;也会去调用getBean来判断是否有A类;关键点就是这一次调用getBean的时候

       

    @Nullable
     2 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
     3     Object singletonObject = this.singletonObjects.get(beanName);
     4     if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
     5         synchronized (this.singletonObjects) {
     6        
     7             singletonObject = this.earlySingletonObjects.get(beanName);
     8             if (singletonObject == null && allowEarlyReference) {
     9                 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    10                 if (singletonFactory != null) {
    11                     singletonObject = singletonFactory.getObject();
    12                     this.earlySingletonObjects.put(beanName, singletonObject);
    13                     this.singletonFactories.remove(beanName);
    14                 }
    15             }
    16         }
    17     }
    18     return singletonObject;
    19 }

    这一次在调用getBean的时候,singletonObjects里面,还是获取不到A类,但是:isSingletonCurrentlyInCreation(beanName)这个判断条件会满足,因为A类在初始化的时候,放到了这个set集合中;

    这个从earlySingletonObjects中获取不到A类,所以,会执行ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);这一行代码,返回singletonFactory中的object;

      2.3 在这一次getBean的时候,A类返回了,所以B类会完成初始化,然后再在A类中注入B类

    3 到这里,循环依赖就完成了,不知道有没有描述清楚:

    我再描述一下把:

       3.1 在A类第一次初始化的时候,会将A类放到表示正在创建的set集合中;将A类放到二级缓存的map中,然后在属性注入的时候,发现A类依赖了B类,会调用getBean,这时候,B类返回的是null,会进行初始化

       3.2 B类初始化的流程和A类初始化流程是一样的,将B类放到表示当前bean正在创建的set集合中,将B类放到二级缓存中,然后进行B类的属性注入,在这是,会发现,B类依赖了A类,然后会调用getBean,这时候,虽然spring单实例池中,还没有A类,但是:二级缓存中有,所以就返回了二级缓存中的A类,让B类完成了初始化流程,

       3.3 B类完成了初始化,那A类依赖的B也就注入了

    最后再贴一张debug的流程图

    红色的是A --> B --> A

    绿色的是 属性注入具体调用的方法

    最后再附上个人在学习spring源码时,在源码上加的注释

    https://github.com/mapy95/spring-sourceCode

  • 相关阅读:
    Python命名空间的本质
    Jetty架构解析及应用示例
    PRML读书会第一章 Introduction(机器学习基本概念、学习理论、模型选择、维灾等)
    PRML读书会第三章 Linear Models for Regression(线性基函数模型、正则化方法、贝叶斯线性回归等)
    PRML读书会第二章 Probability Distributions(贝塔二项式、狄利克雷多项式共轭、高斯分布、指数族等)
    PRML读书会第四章 Linear Models for Classification(贝叶斯marginalization、Fisher线性判别、感知机、概率生成和判别模型、逻辑回归)
    Mac技巧合集第二期
    Mac技巧合集第三期
    C/Java/Python/ObjectiveC在OS X上的性能实验
    程序猿崛起——Growth Hacker
  • 原文地址:https://www.cnblogs.com/mpyn/p/11827489.html
Copyright © 2011-2022 走看看