美文网首页
浅析Spring循环依赖处理

浅析Spring循环依赖处理

作者: _初_chu | 来源:发表于2019-08-09 10:45 被阅读0次

    背景

    正在学习Spring源码。微信公众号的推文中有一篇循环依赖相关文章,但是只有大概处理过程,没有具体的流程。借此机会了解一下循环依赖相关源码,希望能加深自己的理解。(然后就和基友走了几个小时源码...)

    go


    ​ 循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错。下面说一下Spring是如果解决循环依赖的。

    ​ 循环依赖有三种情况

    • setter方式原型,prototype
    • 构造器参数循环依赖
    • setter方式单例,默认方式

    setter方式原型,prototype

    ​ prototype的Bean在容器初始化时,spring不会去检测它的循环依赖问题,因为只有在getBean的实例化过程中才会有循环依赖检查,prototype类型的Bean不会被在被调用时才触发实例化。也就是说不调用它也不会出现循环依赖的报错。

    ​ 首先看一哈报错

    Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'a' defined in class path resource [spring-config-prototype.xml]: Cannot resolve reference to bean 'b' while setting bean property 'b'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'b' defined in class path resource [spring-config-prototype.xml]: Cannot resolve reference to bean 'c' while setting bean property 'c'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'c' defined in class path resource [spring-config-prototype.xml]: Cannot resolve reference to bean 'a' while setting bean property 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:378)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1681)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1433)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:338)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:224)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1115)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:407)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:341)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335)
        at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1123)
        at Test.main(Test.java:35)
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'b' defined in class path resource [spring-config-prototype.xml]: Cannot resolve reference to bean 'c' while setting bean property 'c'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'c' defined in class path resource [spring-config-prototype.xml]: Cannot resolve reference to bean 'a' while setting bean property 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:378)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1681)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1433)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:338)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:367)
        ... 13 more
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'c' defined in class path resource [spring-config-prototype.xml]: Cannot resolve reference to bean 'a' while setting bean property 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:378)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1681)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1433)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:338)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:367)
        ... 21 more
    Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:264)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:367)
        ... 29 more
    

    org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:378

    看一下就具体地方的代码

    @Nullable
    private Object resolveReference(Object argName, RuntimeBeanReference ref) {
       try {
          Object bean;
          //获取引用的Bean名称
          String refName = ref.getBeanName();
          refName = String.valueOf(doEvaluate(refName));
          //如果引用的对象在父类容器中,则从父类容器中获取指定的引用对象
          if (ref.isToParent()) {
             if (this.beanFactory.getParentBeanFactory() == null) {
                throw new BeanCreationException(
                      this.beanDefinition.getResourceDescription(), this.beanName,
                      "Can't resolve reference to bean '" + refName +
                      "' in parent factory: no parent factory available");
             }
             bean = this.beanFactory.getParentBeanFactory().getBean(refName);
          }
          //从当前的容器中获取指定的引用Bean对象,如果指定的Bean没有被实例化
          //则会递归触发引用Bean的初始化和依赖注入
          else {
             bean = this.beanFactory.getBean(refName);
             //将当前实例化对象的依赖引用对象
             this.beanFactory.registerDependentBean(refName, this.beanName);
          }
          if (bean instanceof NullBean) {
             bean = null;
          }
          return bean;
       }
       catch (BeansException ex) {
          throw new BeanCreationException(
                this.beanDefinition.getResourceDescription(), this.beanName,
                "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
       }
    }
    

    ​ debug后找到bean = this.beanFactory.getBean(refName);

    ​ 看一下具体方法

    @Override
    public Object getBean(String name) throws BeansException {
       return doGetBean(name, null, null, false);
    }
    
    //真正实现向IOC容器获取Bean的功能,也是触发依赖注入功能的地方
    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
          @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    
       //根据指定的名称获取被管理Bean的名称,剥离指定名称中对容器的相关依赖
       //如果指定的是别名,将别名转换为规范的Bean名称
       final String beanName = transformedBeanName(name);
       Object bean;
    
       // Eagerly check singleton cache for manually registered singletons.
       //先从缓存中取是否已经有被创建过的单态类型的Bean
       //对于单例模式的Bean整个IOC容器中只创建一次,不需要重复创建
       Object sharedInstance = getSingleton(beanName);
       //IOC容器创建单例模式Bean实例对象
       if (sharedInstance != null && args == null) {
          if (logger.isDebugEnabled()) {
             //如果指定名称的Bean在容器中已有单例模式的Bean被创建
             //直接返回已经创建的Bean
             if (isSingletonCurrentlyInCreation(beanName)) {
                logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                      "' that is not fully initialized yet - a consequence of a circular reference");
             }
             else {
                logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
             }
          }
          //获取给定Bean的实例对象,主要是完成FactoryBean的相关处理
          //注意:BeanFactory是管理容器中Bean的工厂,而FactoryBean是
          //创建创建对象的工厂Bean,两者之间有区别
          bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
       }
    
       else {
          // Fail if we're already creating this bean instance:
          // We're assumably within a circular reference.
          //缓存没有正在创建的单例模式Bean
          //缓存中已经有已经创建的原型模式Bean
          //但是由于循环引用的问题导致实例化对象失败
          if (isPrototypeCurrentlyInCreation(beanName)) {
             throw new BeanCurrentlyInCreationException(beanName);
          }
    
          // Check if bean definition exists in this factory.
          //对IOC容器中是否存在指定名称的BeanDefinition进行检查,首先检查是否
          //能在当前的BeanFactory中获取的所需要的Bean,如果不能则委托当前容器
          //的父级容器去查找,如果还是找不到则沿着容器的继承体系向父级容器查找
          BeanFactory parentBeanFactory = getParentBeanFactory();
          //当前容器的父级容器存在,且当前容器中不存在指定名称的Bean
          if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
             // Not found -> check parent.
             //解析指定Bean名称的原始名称
             String nameToLookup = originalBeanName(name);
             if (parentBeanFactory instanceof AbstractBeanFactory) {
                return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                      nameToLookup, requiredType, args, typeCheckOnly);
             }
             else if (args != null) {
                // Delegation to parent with explicit args.
                //委派父级容器根据指定名称和显式的参数查找
                return (T) parentBeanFactory.getBean(nameToLookup, args);
             }
             else {
                // No args -> delegate to standard getBean method.
                //委派父级容器根据指定名称和类型查找
                return parentBeanFactory.getBean(nameToLookup, requiredType);
             }
          }
    
          //创建的Bean是否需要进行类型验证,一般不需要
          if (!typeCheckOnly) {
             //向容器标记指定的Bean已经被创建
             markBeanAsCreated(beanName);
          }
    
          try {
             //根据指定Bean名称获取其父级的Bean定义
             //主要解决Bean继承时子类合并父类公共属性问题
             final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
             checkMergedBeanDefinition(mbd, beanName, args);
    
             // Guarantee initialization of beans that the current bean depends on.
             //获取当前Bean所有依赖Bean的名称
             String[] dependsOn = mbd.getDependsOn();
             //如果当前Bean有依赖Bean
             if (dependsOn != null) {
                for (String dep : dependsOn) {
                   if (isDependent(beanName, dep)) {
                      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                   }
                   //递归调用getBean方法,获取当前Bean的依赖Bean
                   registerDependentBean(dep, beanName);
                   //把被依赖Bean注册给当前依赖的Bean
                   getBean(dep);
                }
             }
    
             // Create bean instance.
             //创建单例模式Bean的实例对象
             if (mbd.isSingleton()) {
                //这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象
                sharedInstance = getSingleton(beanName, () -> {
                   try {
                      //创建一个指定Bean实例对象,如果有父级继承,则合并子类和父类的定义
                      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.
                      //显式地从容器单例模式Bean缓存中清除实例对象
                      destroySingleton(beanName);
                      throw ex;
                   }
                });
                //获取给定Bean的实例对象
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
             }
    
             //IOC容器创建原型模式Bean实例对象
             else if (mbd.isPrototype()) {
                // It's a prototype -> create a new instance.
                //原型模式(Prototype)是每次都会创建一个新的对象
                Object prototypeInstance = null;
                try {
                   //回调beforePrototypeCreation方法,默认的功能是注册当前创建的原型对象
                   beforePrototypeCreation(beanName);
                   //创建指定Bean对象实例
                   prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                   //回调afterPrototypeCreation方法,默认的功能告诉IOC容器指定Bean的原型对象不再创建
                   afterPrototypeCreation(beanName);
                }
                //获取给定Bean的实例对象
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
             }
    
             //要创建的Bean既不是单例模式,也不是原型模式,则根据Bean定义资源中
             //配置的生命周期范围,选择实例化Bean的合适方法,这种在Web应用程序中
             //比较常用,如:request、session、application等生命周期
             else {
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                //Bean定义资源中没有配置生命周期范围,则Bean定义不合法
                if (scope == null) {
                   throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                   //这里又使用了一个匿名内部类,获取一个指定生命周期范围的实例
                   Object scopedInstance = scope.get(beanName, () -> {
                      beforePrototypeCreation(beanName);
                      try {
                         return createBean(beanName, mbd, args);
                      }
                      finally {
                         afterPrototypeCreation(beanName);
                      }
                   });
                   //获取给定Bean的实例对象
                   bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                   throw new BeanCreationException(beanName,
                         "Scope '" + scopeName + "' is not active for the current thread; consider " +
                         "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                         ex);
                }
             }
          }
          catch (BeansException ex) {
             cleanupAfterBeanCreationFailure(beanName);
             throw ex;
          }
       }
    
       // Check if required type matches the type of the actual bean instance.
       //对创建的Bean实例对象进行类型检查
       if (requiredType != null && !requiredType.isInstance(bean)) {
          try {
             T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
             if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
             }
             return convertedBean;
          }
          catch (TypeMismatchException ex) {
             if (logger.isDebugEnabled()) {
                logger.debug("Failed to convert bean '" + name + "' to required type '" +
                      ClassUtils.getQualifiedName(requiredType) + "'", ex);
             }
             throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
          }
       }
       return (T) bean;
    }
    

    ​ 循环依赖的判断在创建Bean之前。

    ​ 能看到有这么一个判断。

    if (isPrototypeCurrentlyInCreation(beanName)) {
             throw new BeanCurrentlyInCreationException(beanName);
          }
    
    /** Names of beans that are currently in creation */
        private final ThreadLocal<Object> prototypesCurrentlyInCreation =
                new NamedThreadLocal<>("Prototype beans currently in creation");
    
    protected boolean isPrototypeCurrentlyInCreation(String beanName) {
       Object curVal = this.prototypesCurrentlyInCreation.get();
       return (curVal != null &&
             (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
    }
    

    ​ 大致是,创建Bean时会进行判断,如果在当前线程的ThreadLocal取出的Set中找到同名的beanName则认为出现循环依赖。

    ​ 判断时从Set中取值,是在哪放进去的呢,又是在哪被移除的呢?

    ​ 在上面的doGetBean中能找到如下片段

    else if (mbd.isPrototype()) {
       // It's a prototype -> create a new instance.
       //原型模式(Prototype)是每次都会创建一个新的对象
       Object prototypeInstance = null;
       try {
          //回调beforePrototypeCreation方法,默认的功能是注册当前创建的原型对象
          beforePrototypeCreation(beanName);
          //创建指定Bean对象实例
          prototypeInstance = createBean(beanName, mbd, args);
       }
       finally {
          //回调afterPrototypeCreation方法,默认的功能告诉IOC容器指定Bean的原型对象不再创建
          afterPrototypeCreation(beanName);
       }
       //获取给定Bean的实例对象
       bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
    }
    
    /**
     * Callback before prototype creation.
     * <p>The default implementation register the prototype as currently in creation.
     * @param beanName the name of the prototype about to be created
     * @see #isPrototypeCurrentlyInCreation
     */
    @SuppressWarnings("unchecked")
    protected void beforePrototypeCreation(String beanName) {
       Object curVal = this.prototypesCurrentlyInCreation.get();
       if (curVal == null) {
          this.prototypesCurrentlyInCreation.set(beanName);
       }
       else if (curVal instanceof String) {
          Set<String> beanNameSet = new HashSet<>(2);
          beanNameSet.add((String) curVal);
          beanNameSet.add(beanName);
          this.prototypesCurrentlyInCreation.set(beanNameSet);
       }
       else {
          Set<String> beanNameSet = (Set<String>) curVal;
          beanNameSet.add(beanName);
       }
    }
    
    /**
         * Callback after prototype creation.
         * <p>The default implementation marks the prototype as not in creation anymore.
         * @param beanName the name of the prototype that has been created
         * @see #isPrototypeCurrentlyInCreation
         */
        @SuppressWarnings("unchecked")
        protected void afterPrototypeCreation(String beanName) {
            Object curVal = this.prototypesCurrentlyInCreation.get();
            if (curVal instanceof String) {
                this.prototypesCurrentlyInCreation.remove();
            }
            else if (curVal instanceof Set) {
                Set<String> beanNameSet = (Set<String>) curVal;
                beanNameSet.remove(beanName);
                if (beanNameSet.isEmpty()) {
                    this.prototypesCurrentlyInCreation.remove();
                }
            }
        }
    

    ​ 现在就比较清晰了,在每个prototype类型的Bean被创建之前,首先会进行循环依赖的判断,通过判断的每个Bean会暂存在一个Set(只存创建中的Bean,创建完就remove了)中记录。

    ​ 以A, B, C为例

    public class Test {
        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        public static class A {
            B b;
        }
        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        public static class B {
            C c;
        }
        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        public static class C {
            A a;
        }
    
        public static void main(String[] args) {
            ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-config-prototype.xml");
            //ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-config-construct.xml");
            //ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-config-setter.xml");
    
            System.out.println(classPathXmlApplicationContext.getBean(A.class));
        }
    }
    
    1. A通过了判断记录并进行createBean,发现需要引用B

    2. 那么就需要创建B,B也通过了,被记录在册,并进行对C的创建

    3. C也通过了,被记录并开始创建,可是C需要A,那么试图去创建A

    4. 当再次试图创建A的时候,发现A已经被记录在册了呀,眉头一皱,发现问题并顺手抛了个异常

    ​ 虽然Spring初始化的过程是单线程的,但是后续的getBean并不能保证是同一线程,所以把记录的Set存在个自的线程中。


    ​ 另外两种整体上的流程是一样的,具体走的地方有差异。

    构造器参数循环依赖

    ​ 用constructor的Bean默认是单例,在容器初始化时就会触发Bean实例化,所以启动时就会检测到循环依赖问题。

    ​ 单例的所以在doGetBean中走Singleton的判断

    // Create bean instance.
    //mbd这玩意是BeanDefinition,由资源文件解析而来,记录Bean的定义信息的
    if (mbd.isSingleton()) {
       sharedInstance = getSingleton(beanName, () -> {
          try {
             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);
    }
    

    ​ 进入

    /**
     * Return the (raw) singleton object registered under the given name,
     * creating and registering a new one if none registered yet.
     * @param beanName the name of the bean
     * @param singletonFactory the ObjectFactory to lazily create the singleton
     * with, if necessary
     * @return the registered singleton object
     */
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
       Assert.notNull(beanName, "Bean name must not be null");
       synchronized (this.singletonObjects) {
          Object singletonObject = this.singletonObjects.get(beanName);
          if (singletonObject == null) {
             if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName,
                      "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                      "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
             }
             if (logger.isDebugEnabled()) {
                logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
             }
            //这里
             beforeSingletonCreation(beanName);
             boolean newSingleton = false;
             boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
             if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
             }
             try {
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
             }
             catch (IllegalStateException ex) {
                // Has the singleton object implicitly appeared in the meantime ->
                // if yes, proceed with it since the exception indicates that state.
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                   throw ex;
                }
             }
             catch (BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                   for (Exception suppressedException : this.suppressedExceptions) {
                      ex.addRelatedCause(suppressedException);
                   }
                }
                throw ex;
             }
             finally {
                if (recordSuppressedExceptions) {
                   this.suppressedExceptions = null;
                }
                afterSingletonCreation(beanName);
             }
             if (newSingleton) {
                addSingleton(beanName, singletonObject);
             }
          }
          return singletonObject;
       }
    }
    

    ​ 通过debug异常在beforeSingletonCreation(beanName);抛出

    /** Names of beans that are currently in creation. */
        private final Set<String> singletonsCurrentlyInCreation =
                Collections.newSetFromMap(new ConcurrentHashMap<>(16));
    
    /**
     * Callback before singleton creation.
     * <p>The default implementation register the singleton as currently in creation.
     * @param beanName the name of the singleton about to be created
     * @see #isSingletonCurrentlyInCreation
     */
    protected void beforeSingletonCreation(String beanName) {
       if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
          throw new BeanCurrentlyInCreationException(beanName);
       }
    }
    

    ​ emmm熟悉的味道,基本和prototype的流程一样


    setter方式单例,默认方式

    ​ 这种方式和另外两种的区别在于,用这种方式时,不会出现循环依赖的异常,也就是说容器帮我们解决了循环依赖的问题。

    ​ 这种方式下,Bean还是默认的单例,所以流程应该是和constructor类似的。

    ​ 通过debug,这两种方式流程的确类似,区别在于doGetBean中

    // Eagerly check singleton cache for manually registered singletons.
    // 这里constructor返回null,setter则能返回对象
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
       if (logger.isTraceEnabled()) {
          ...
    }
    else {
      ...
    }
    

    ​ 进入

    /** Cache of singleton factories: bean name to ObjectFactory. */
        private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    @Override
    @Nullable
    public Object getSingleton(String beanName) {
       return getSingleton(beanName, true);
    }
    
    /**
         * Return the (raw) singleton object registered under the given name.
         * <p>Checks already instantiated singletons and also allows for an early
         * reference to a currently created singleton (resolving a circular reference).
         * @param beanName the name of the bean to look for
         * @param allowEarlyReference whether early references should be created or not
         * @return the registered singleton object, or {@code null} if none found
         */
        @Nullable
        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) {
              // 在出现循环依赖时,这个地方出现了区别
              // constructor 的 singletonFactories 是空的,所以取不到值
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
            return singletonObject;
        }
    

    ​ 找到差异的地方就有头绪了,只要找到setter方式时什么时候在singletonFactories中放入数据的就行

    ​ 根据调用链,首先在之前的doGetBean中

    // Create bean instance.
    if (mbd.isSingleton()) {
       sharedInstance = getSingleton(beanName, () -> {
          try {
             //注意这个方法
             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);
    }
    

    AbstractAutowireCapableBeanFactory

    /**
     * Central method of this class: creates a bean instance,
     * populates the bean instance, applies post-processors, etc.
     * @see #doCreateBean
     */
    @Override
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
          throws BeanCreationException {
    
       ...
       try {
            //现在这里
          Object beanInstance = doCreateBean(beanName, mbdToUse, args);
          if (logger.isTraceEnabled()) {
             logger.trace("Finished creating instance of bean '" + beanName + "'");
          }
          return beanInstance;
       }
      final Object bean = instanceWrapper.getWrappedInstance();
            Class<?> beanType = instanceWrapper.getWrappedClass();
            if (beanType != NullBean.class) {
                mbd.resolvedTargetType = beanType;
            }
            synchronized (mbd.postProcessingLock) {
                if (!mbd.postProcessed) {
                    try {
                        applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                    }
                    catch (Throwable ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Post-processing of merged bean definition failed", ex);
                    }
                    mbd.postProcessed = true;
                }
            }
            boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                    isSingletonCurrentlyInCreation(beanName));
            if (earlySingletonExposure) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Eagerly caching bean '" + beanName +
                            "' to allow for resolving potential circular references");
                }
          //在这边进行add
                addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
            }
       ...
    }
    

    AbstractAutowireCapableBeanFactory

    /**
     * Actually create the specified bean. Pre-creation processing has already happened
     * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
     * <p>Differentiates between default bean instantiation, use of a
     * factory method, and autowiring a constructor.
     * @param beanName the name of the bean
     * @param mbd the merged bean definition for the bean
     * @param args explicit arguments to use for constructor or factory method invocation
     * @return a new instance of the bean
     * @throws BeanCreationException if the bean could not be created
     * @see #instantiateBean
     * @see #instantiateUsingFactoryMethod
     * @see #autowireConstructor
     */
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
          throws BeanCreationException {
    
       // Instantiate the bean.
       BeanWrapper instanceWrapper = null;
       if (mbd.isSingleton()) {
          instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
       }
       if (instanceWrapper == null) {
          //接着到这
          instanceWrapper = createBeanInstance(beanName, mbd, args);
       }
       ...
    }
    
    /**
     * Create a new instance for the specified bean, using an appropriate instantiation strategy:
     * factory method, constructor autowiring, or simple instantiation.
     * @param beanName the name of the bean
     * @param mbd the bean definition for the bean
     * @param args explicit arguments to use for constructor or factory method invocation
     * @return a BeanWrapper for the new instance
     * @see #obtainFromSupplier
     * @see #instantiateUsingFactoryMethod
     * @see #autowireConstructor
     * @see #instantiateBean
     */
    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
       ...
         
       // Candidate constructors for autowiring?
       // mbd 这个中存了Bean的定义信息,从中取出bean的构造器,判断,setter不会进入autowireConstructor
       Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
       if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
             mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
          return autowireConstructor(beanName, mbd, ctors, args);
       }
    
       // Preferred constructors for default construction?
       ctors = mbd.getPreferredConstructors();
       if (ctors != null) {
          return autowireConstructor(beanName, mbd, ctors, null);
       }
    
       // No special handling: simply use no-arg constructor.
       return instantiateBean(beanName, mbd);
    }
    

    ​ 现在就很清楚了,constructor方式会进入autowireConstructor,然后在这里进入A->B->C->A的过程,只存下了在创建过程中的Bean的name,判断出现循环依赖直接抛异常

    ​ 而setter会在bean实例的过程中会先把单例工厂存下来,在进入A->B->C->A的过程时,当C再次依赖需要A时,容器会从之前存下来的单例工厂中取出A的单例实例(虽然这个A还没有进行具体赋值)。当C得到A后循环依赖也就解决了,可以继续后续步骤了。

    ​ 整个过程是三级缓存

    ​ 首先从singletonObjects(一级缓存)中尝试获取,如果获取不到并且对象在创建中,则尝试从earlySingletonObjects(二级缓存)中获取,如果还是获取不到并且允许从singletonFactories通过getObject获取,则通过singletonFactory.getObject()(三级缓存)获取

    后记

    ​ 想了一个很蠢的问题,prototype是没办法。但是为什么setter有办法解决循环依赖,constructor不解决呢?然后发现= =setter可以先从单例工厂创建一个空的暂时用着,constructor的话创都创不出来,解决毛哟。

    ​ 这边只展示了大概流程思路,有兴趣的同学可以再深入去看一下。

    相关文章

      网友评论

          本文标题:浅析Spring循环依赖处理

          本文链接:https://www.haomeiwen.com/subject/isecjctx.html