zoukankan      html  css  js  c++  java
  • Spring容器如何解决循环依赖的原理

    Spring容器如何解决循环依赖的原理

    最近看源码在研究类似如下配置的循环依赖是怎么解决的?

    1 <bean id="a" class="com.project.demo.A" scope="singleton">
    2       <property name="b" ref="b"/>
    3 </bean>
    4 <bean id="b" class="com.project.demo.B" scope="singleton">
    5       <property name="a" ref="a"/>
    6 </bean>

    说明:

      1、Spring容器解决循环依赖的问题配置类必须是单例模式scope="singleton"才支持,如果是scope="prototype"是无法解决循环依赖的。

      2、Spring容器解决循环依赖主要依靠三级缓存机制

        2.1 一级缓存使用的map: private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);

        2.2 二级缓存使用的map: private final Map<String, Object> earlySingletonObjects = new HashMap(16); 

        2.3 三级缓存使用的map: private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);

      3、Spring容器解决循环依赖简洁概述主要有四大流程方法:获取对象 getSingleton()、 创建对象(实例化) doCreateBean()、填充属性(初始化) populateBean()、返回对象 addSingleton()

    在系统启动获取配置文件后,程序是依次读取并加载的,所以当执行上面配置文件时,先获取a对象,如果获取不到则先实例化a对象,然后初始化a对象即给a添加b属性,在添加b属性的时候先获取b,如果b存在则直接给a设置,不存在则实例化b,并且初始化b对象即给b添加对应的属性。

    那么在代码执行过程中,先调用getSingleton()方法,我们查看源码

     1     @Nullable
     2     public Object getSingleton(String beanName) {
    // 调用下方重载方法
    3 return this.getSingleton(beanName, true); 4 } 5 6 @Nullable 7 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
           // 先从一级缓存中获取a对象的实例
    8 Object singletonObject = this.singletonObjects.get(beanName);
    // 如果从一级缓存中获取不到a对象,那么检查该对象是否正在被创建,如果正在被创建,则进入if循环中
    9 if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { 10 synchronized(this.singletonObjects) {
                // 从二级缓存中获取该对象
    11 singletonObject = this.earlySingletonObjects.get(beanName);
    // 如果二级缓存中无法获取该对象,那么一定会进入如下if方法,因为allowEarlyReference传过来的时候就是true
    12 if (singletonObject == null && allowEarlyReference) {
                  // 从三级缓存中获取该对象
    13 ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName); 14 if (singletonFactory != null) {
    // 如果获取到了该对象,就将三级缓存中的对象放到二级缓存中,并且将三级缓存中的对象删除
    15 singletonObject = singletonFactory.getObject(); 16 this.earlySingletonObjects.put(beanName, singletonObject); 17 this.singletonFactories.remove(beanName); 18 } 19 } 20 } 21 } 22 23 return singletonObject; 24 }

    从三面的源码发现,如果a第一次获取,那么第9行的if语句为false,将直接放回为null,这时回到创建对象doCreateBean()方法,该方法使用反射的方式生成a对象,并且该对象在三级缓存中,对象生成后就需要对a对象进行属性填充:

     1 protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
     2         // 省略多行代码,大致就是调用各种方法,通过反射创建对象
     3         try {
     4             // a对象创建完成,调用属性填充方法,对a进行属性填充
     5             this.populateBean(beanName, mbd, instanceWrapper);
     6             exposedObject = this.initializeBean(beanName, exposedObject, mbd);
     7         } catch (Throwable var18) {
     8             if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
     9                 throw (BeanCreationException)var18;
    10             }
    11 
    12             throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
    13         }
    14 
    15         if (earlySingletonExposure) {
    16             Object earlySingletonReference = this.getSingleton(beanName, false);
    17             if (earlySingletonReference != null) {
    18                 // 省略多行代码
    19             }
    20         }
    22         // 省略多行代码
    23     }

    在上面代码doCreateBean()方法中先创建a对象,创建完成后会调用this.populateBean(beanName, mbd, instanceWrapper)方法对a进行属性填出,这个时候会获取配置文件中所有<bean id="a">里面的所有属性,发现会存在一个b属性,下面贴出部分源码

     1 protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
     2         if (bw == null) {
     3             if (mbd.hasPropertyValues()) {
     4                 throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
     5             }
     6         } else {
     7             boolean continueWithPropertyPopulation = true;
     8             if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
     9                 // 删除大量代码
    10             }
    11 
    12             if (continueWithPropertyPopulation) {
    13                 // 删除大量源代码,applyPropertyValues方法中beanName为a,pvs为状态各种属性的PropertyValues对象,pvs就装有b这个属性
    14                 if (pvs != null) {
    15                     this.applyPropertyValues(beanName, mbd, bw, (PropertyValues)pvs);
    16                 }
    17 
    18             }
    19         }
    20     }

    继续跟进applyPropertyValues方法的源码

     1 protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
     2         if (!pvs.isEmpty()) {
     3             if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
     4                 ((BeanWrapperImpl)bw).setSecurityContext(this.getAccessControlContext());
     5             }
     6             MutablePropertyValues mpvs = null;
     7             List original;
     8             if (pvs instanceof MutablePropertyValues) {
     9                 // 省略大量代码
    10             } else {
    11                 original = Arrays.asList(pvs.getPropertyValues());
    12             }
    13             // 省略大量代码 大致过程是将属性对象pvs 转化成original List对象,然后在使用迭代器在下面进行迭代
    14             Iterator var11 = original.iterator();
    15             while(true) {
    16                 while(var11.hasNext()) {
    17                     PropertyValue pv = (PropertyValue)var11.next();
    18                     if (pv.isConverted()) {
    19                         deepCopy.add(pv);
    20                     } else {
    21                         String propertyName = pv.getName();
    22                         Object originalValue = pv.getValue();
    23                         // 通过下面方法解决依赖的b,整个方法在迭代器中,外层在while(true)中,可能有多个属性,循环直到所有属性都解决了就return;或者抛出异常
    24                         Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
    25                         // 省略大量代码
    26                     }
    27                 }
    28 
    29                 if (mpvs != null && !resolveNecessary) {
    30                     mpvs.setConverted();
    31                 }
    32 
    33                 try {
    34                     bw.setPropertyValues(new MutablePropertyValues(deepCopy));
    35                     return;
    36                 } catch (BeansException var19) {
    37                     throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", var19);
    38                 }
    39             }
    40         }
    41     }

    继续跟进上面红色方法

    1 public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
    2         if (value instanceof RuntimeBeanReference) {
    3             RuntimeBeanReference ref = (RuntimeBeanReference)value;
    4             return this.resolveReference(argName, ref);
    5         } else if (value instanceof RuntimeBeanNameReference) {
    6             // 省略多行代码
    7         } 
    8         // 省略多行代码
    9     }

    继续跟进红色部分的代码

     1 private Object resolveReference(Object argName, RuntimeBeanReference ref) {
     2         try {
     3             String refName = ref.getBeanName();
     4             refName = String.valueOf(this.doEvaluate(refName));
     5             Object bean;
     6             if (ref.isToParent()) {
     7                 // 省略多行代码
     8             } else {
     9                 // 通过refName的值b又去工厂找b对象
    10                 bean = this.beanFactory.getBean(refName);
    11                 this.beanFactory.registerDependentBean(refName, this.beanName);
    12             }
    13 
    14             // 省略多行代码
    15             return bean;
    16         } catch (BeansException var5) {
    17             throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName, "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, var5);
    18         }
    19     }
    1     public Object getBean(String name) throws BeansException {
    2         return this.doGetBean(name, (Class)null, (Object[])null, false);
    3     }
        protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
            String beanName = this.transformedBeanName(name);
            // 跟了这么就,最终表明在实例化a对象后,在装载a的属性b时,会经过各种校验最终到getSingleton(),及先获取b对象,如果b对象不存在则会对b完成创建的过程
            Object sharedInstance = this.getSingleton(beanName);
            // 省略多行代码
        }

    上面整个过程概括:在检测到需要实例化a时,先去获取a对象,看a是否已经存在,获取去先从一级缓存中获取,如果没有并且如果a也没有正在实例化,那么直接返回null,表明获取不到a对象,那么此时调用doCreateBean()方法完成对a对象的实例化过程(通过反射创建a对象),并且将创建的a对象放在三级缓存中,然后继续执行doCreateBean中的populateBean()方法完成对a进行初始化即添加属性b,经过一些列校验,最终又会调用getSingleton()方法来获取b对象,同样会返回null,这个时候就会去执行doCreateBean()方法创建b对象,同样过反射创建b,当b对象创建完成时也会存放在三级缓存中,在实例化b对象完成,然后继续执行doCreateBean中的populateBean()方法,也需要初始化b对象,填充b的属性,这时发现b对象的属性是a,同样再次通过getSingleton()方法获取a,获取a的过程如第一个源码部分,先从一级缓存中获取,获取不到,然后判断a正在创建中,然后就从二级、三级缓存中获取,最终在三级缓存中获取到了a,并且将三级缓存中的a对象放到二级缓存中,并将删除三级缓存中的a,此时b对象初始化也完成。

    在a对象初始化的流程中,将b对象也实例化和初始化了,在b的初始化过程中,将a从三级缓存移到了二级缓存中,当b初始化完成后继续向下执行,会执行到addSingleton(),查看源码

        protected void addSingleton(String beanName, Object singletonObject) {
            synchronized(this.singletonObjects) {
           // 将b加入到1级缓存
    this.singletonObjects.put(beanName, singletonObject);
    // 将b从三级缓存中删除
    this.singletonFactories.remove(beanName);
    // 将b从二级缓存中删除(b在二级缓存中没有,即空删除)
    this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }

    从上面代码可以看出在,并创建完成并且初始化后,会将B从三级缓存中直接放到一级缓存中,并且删除三级缓存中的数据。

    所有b工作做完后返回到a初始化属性b的代码

     1     protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
     2 
     3         // 省略大量代码
     4         try {
     5             // 完成a对于属性b的检查装配工作后返回到方法populateBean(),继续向下执行
     6             this.populateBean(beanName, mbd, instanceWrapper);
     7             exposedObject = this.initializeBean(beanName, exposedObject, mbd);
     8         } catch (Throwable var18) {
     9             if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
    10                 throw (BeanCreationException)var18;
    11             }
    12 
    13             throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
    14         }
    15 
    16         if (earlySingletonExposure) {
    17             // a执行到该方法getSingleton(),这里面会将a从二级缓存中取出来
    18             Object earlySingletonReference = this.getSingleton(beanName, false);
    19             if (earlySingletonReference != null) {
    20                 // 省略大量代码
    21             }
    22         }
    23         // 省略大量代码
    24     }

    继续执行,最终也会到addSingleton()方法,将a也加入一级缓存,并且从二级缓存中删除a。

    这样a和b两个循环依赖的bean都被放入到一级缓存中。

  • 相关阅读:
    oracle数据泵导入导出命令
    深入理解JVM—JVM内存模型
    JVM 内存初学 堆(heap)、栈(stack)和方法区(method)
    Java中Comparable和Comparator接口区别分析
    Java中的匿名内部类总结
    Git版本控制软件结合GitHub从入门到精通常用命令学习手册
    Git客户端图文详解如何安装配置GitHub操作流程攻略
    Java内部类详解
    Java类加载原理解析
    OracleDBconsoleorcl服务无法启动的原因及解决思路
  • 原文地址:https://www.cnblogs.com/yanzige/p/14180582.html
Copyright © 2011-2022 走看看