• Spring源码-循环依赖源码解读


    Spring源码-循环依赖源码解读


      笔者最近无论是看书还是从网上找资料,都没发现对Spring源码是怎么解决循环依赖这一问题的详解,大家都是解释了Spring解决循环依赖的想法(有的解释也不准确,在《Spring源码深度解析》作者也是看别人的博客说明了一下),没有从源码的角度分析是怎么解决循环依赖的,笔者就把自己看源码的过程写一下。

      写这一篇文章算是个引路的,Spring为了程序的健壮性做了大量分析校验,调用的方法繁多复杂,我这篇文章为读者清理出解决循环依赖的流程。

    Spring中对象可以配置成单例模式也可配置为原型模式(原型模式很值得一看)。
    Spring中可以通过构造函数注入、setter注入的方式来解决对象与对象间的依赖。
    对象间的循环依赖只能配置单例、setter注入的方式来解决,其他方法就会报错,下面我们通过源码分析一下。

    一、单例、setter注入解决循环依赖

    假如有TestA、TestB、TestC三个对象,其中TestA依赖TestB,TestB依赖TestC,TestC依赖TestA。

    下面具体通过代码分析Spring是如何解决单例通过Setter注入的循环依赖。
    在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry类中有几个集合类型的成员变量,用来做缓存用的需要特别留意,源码如下:

    public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
            ......
        private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
        private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
        private final Map<String, Object> earlySingletonObjects = new HashMap(16);
        private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16));
        ......
    }

    上面的代码中:
    singletonsCurrentlyInCreation:保存对象的BeanName,在创建对象之前就会把对象的beanName保存起来。
    singletonFactories:保存对象的BeanName和创建bean的工厂AbstractAutowireCapableBeanFactory(ObjectFactory),(对象的构造函数是在这一步完成的)
    earlySingletonObjects:保存对象BeanName和对象的早期实例(ObjectFactory#getObject得到的对象)(此时对象还没注入属性),此时可以作为对象填充依赖。
    singletonObjects:保存BeanName和bean的实例(此时对象已经完成了属性注入)

    通过Spring获取testA对象:

    通过Spring获取testA对象
    ApplicationContext factory=new ClassPathXmlApplicationContext("classpath:applicationContext-beans.xml");
    TestA testA = (TestA) factory.getBean("testA");
    System.out.println(testA);

    factory.getBean最终调用的是org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

    下面看doGetBean源码:

    protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
      ......
      //重点1
      Object sharedInstance = this.getSingleton(beanName);
      
      ......
      
        if (mbd.isSingleton()) {
            //重点2
            sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                @Override
                public Object getObject() throws BeansException {
                    try {
                        //重点3
                        return createBean(beanName, mbd, args);
                    }
                    catch (BeansException ex) {
                        // Explicitly remove instance from singleton cache: It might have been put there
                        // eagerly by the creation process, to allow for circular reference resolution.
                        // Also remove any beans that received a temporary reference to the bean.
                        destroySingleton(beanName);
                        throw ex;
                    }
                }
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        }
    }

    在上面源码中:

    1、重点1:

      //重点1 根据beanName试图从缓存中获取已经创建的对象,第一次进入是肯定返回null,这个函数放在后面解释。

    2、重点2:

      在//重点2中才是真正创建TestA对象的方法,下面是//重点2 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton的源码:

    //重点2
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            ......
            //重点2-1
            beforeSingletonCreation(beanName);
            ......
            try {
                //重点2-2
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }catch (IllegalStateException ex) {
                ......
            }catch (BeanCreationException ex) {
                ......
            }
            finally {
                //重点2-3
                afterSingletonCreation(beanName);
            }
        
        }
    }

      在//重点2-1中beforeSingletonCreation方法中只做了一件事,就是保存beanName到singletonsCurrentlyInCreation(注意),这个时候testA就保存在singletonsCurrentlyInCreation里面了,源码如下:

    //重点2-1
    protected void beforeSingletonCreation(String beanName) {
        //this.singletonsCurrentlyInCreation.add(beanName)
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }

      在 //重点2-2 会调用 //重点3 。

    3、重点3:

      在//重点3中org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean调用doCreateBean,doCreateBean方法源码:

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
        ......
        //重点3-1
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isDebugEnabled()) {
                logger.debug("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
            //重点3-2
            addSingletonFactory(beanName, new ObjectFactory<Object>() {
                @Override
                public Object getObject() throws BeansException {
                    //重点3-3
                    return getEarlyBeanReference(beanName, mbd, bean);
                }
            });
        }
        ......
      
    }

      在//重点3-1判断testA必须是单例,并存在在singletonsCurrentlyInCreation中,此时才会调用//重点3-2的addSingletonFactory方法,//重点3-2的addSingletonFactory方法源码,

    //重点3-2
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
            
                //重点3-2-1
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                
                this.registeredSingletons.add(beanName);
            }
        }
    }

      在上面的代码中有一个singletonFactory的参数,这个参数是//重点3-3调用getEarlyBeanReference得到的,getEarlyBeanReference返回的就是ObjectFactory对象,是完成构造方法的。

      在//重点3-2-1 向singletonFactories添加 ObjectFactory(注意),这个时候,testA和testA的ObjectFactory对象保存在singletonFactories,并移除earlySingletonObjects(现在earlySingletonObjects里面并没有testA)。

      执行完//重点3-2,发现testA依赖TestB对象,此时会递归调用getBean获取TestB,testB执行步骤和上面testA一样,然后testB依赖TestC,递归调用TestC,此时singletonFactories里面保存的数据如下:
      testA -> ObjectFactory(TestA)
      testB -> ObjectFactory(TestB)
      testC -> ObjectFactory(TestC)

      创建testC过程中执行完//重点3-2,发现依赖testA,此时会递归调用getBean获取TestA,这时候执行到//重点1,下面开始解析//重点1 Object sharedInstance = getSingleton(beanName);方法。

    4、重点1:

      下面是调用//重点1 getSingleton(beanName),调用getSingleton(beanName, true)的源码:

    //重点1
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    //重点1-1
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                    
                        //重点1-2
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }

      在上面代码中isSingletonCurrentlyInCreation就是判断对象(testA)是否被保存过(在//重点2-1的时候testA就被保存了)

      在//重点1-1中,从singletonFactories缓存中获取到ObjectFactory(TestA),

      在//重点1-2并通过模板模式获取TestA对象,保存在earlySingletonObjects缓存中,并移除singletonFactories中的testA,

      此时testC获取到TestA的早期对象,可以注入TestA对象,自此TestC完成依赖注入,并把testC保存到singletonObjects中。

      TestC创建完成,返回给testB,testB也算是完成了创建,然后返回给testA,自此循环依赖算是完成了。

    总结:

      单例的setter注入,会先把创建testA、testB、testC对象的ObjectFactory(对象工厂)保存在singletonFactories里面,然后testC依赖testA,那就从singletonFactories缓存中拿到testA的ObjectFactory,通过ObjectFactory的getObject获取TestA的对象,并保存在earlySingletonObjects缓存中,清除singletonFactories缓存中的testA,此时testC就可以获取earlySingletonObjects缓存中TestA的对象,完成注入TestA的过程。TestC对象创建完成就可以注入到TestB对象中,然后TestB注入到TestA中。

      singletonsCurrentlyInCreation就是用来保存是否试图创建某个对象的beanName,不管有没有创建成功,为后来从singletonFactories缓存中或earlySingletonObjects缓存中取值做个标识。

    二、单利、构造函数注入循环依赖

       假如有TestA、TestB两个对象,TestA依赖TestB,TestB依赖TestA;

      构造函数注入和setter注入的不同在于,构造函数注入无法先调用构造函数实例化对象,当TestA依赖TestB,会先把testA保存到singletonsCurrentlyInCreation中,然后getBean("testB"),然后把testB保存到singletonsCurrentlyInCreation中,发现TestB依赖TestA,然后再getBean("testA"),此时执行下面的代码(和模块一,重点2是同一块代码):

    //重点2 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton的源码:
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        //重点2-0
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            ......
            //重点2-1
            beforeSingletonCreation(beanName);
            ......
            try {
                //重点2-2
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }catch (IllegalStateException ex) {
                ......
            }catch (BeanCreationException ex) {
                ......
            }
            finally {
                //重点2-3
                afterSingletonCreation(beanName);
            }
        
        }
    }

      在上面的代码中 //重点2-0  Object singletonObject = this.singletonObjects.get(beanName); 此时singletonObject为空会执行beforeSingletonCreation方法,源码如下:

    //重点2-1
    protected void beforeSingletonCreation(String beanName) {
        //this.singletonsCurrentlyInCreation.add(beanName)
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }

      结果发现singletonsCurrentlyInCreation已经存在testA,抛出BeanCurrentlyInCreationException

    setter注入为什么不会执行这一步呢,因为setter注入中会实例化TestA、TestB保存在缓存中,所以在执行 //重点2-0  Object singletonObject = this.singletonObjects.get(beanName); 时 singletonObject 不为空,并不会执行beforeSingletonCreation所以不会保存。

    三、原型模式下循环依赖

      假如有TestA、TestB两个对象,TestA依赖TestB,TestB依赖TestA,当调用getBean("testA")时,会先把beanName(testA)保存到isPrototypeCurrentlyInCreation里面,发现TestA依赖TestB,就会去getBean("testB"),然后把beanName(testB)也保存到isPrototypeCurrentlyInCreation里面,此时TestB发现依赖TestA,去getBean("testA")时,发现isPrototypeCurrentlyInCreation已经存在testA,就会抛出BeanCurrentlyInCreationException异常,具体代码如下

      factory.getBean最终调用的是org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean,

    下面看doGetBean源码:

    protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
      ......
      if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
      ......
    }
    
    //isPrototypeCurrentlyInCreation源码
    protected boolean isPrototypeCurrentlyInCreation(String beanName) {
        Object curVal = this.prototypesCurrentlyInCreation.get();
        return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
    }

  • 相关阅读:
    C# 任务、线程、同步(五)
    C# 任务、线程、同步(四)
    C# 得到本周的第一天和最后一天
    C# Datatable 转实体对象
    C# 任务、线程、同步(三)
    apache的工作模式 和 最大连接数设置
    MySQL中的配置参数interactive_timeout和wait_timeout(可能导致过多sleep进程的两个参数)
    上传图片到另外的服务器
    Brackets
    centos 安装mysql 5.5.12
  • 原文地址:https://www.cnblogs.com/java-zzl/p/10025609.html
Copyright © 2020-2023  润新知