zoukankan      html  css  js  c++  java
  • 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))));
    }

  • 相关阅读:
    remove white space from read
    optimize the access speed of django website
    dowload image from requests
    run jupyter from command
    crawl wechat page
    python version 2.7 required which was not found in the registry windows 7
    health
    alternate rows shading using conditional formatting
    word
    【JAVA基础】static 关键字
  • 原文地址:https://www.cnblogs.com/java-zzl/p/10025609.html
Copyright © 2011-2022 走看看