美文网首页
追溯IOC循环依赖处理

追溯IOC循环依赖处理

作者: 修伊Dal | 来源:发表于2021-07-31 17:07 被阅读0次

    追溯IOC循环依赖处理

    前言

    循环依赖是指多个类存在直接或间接依赖关系,最终形成一个闭环调用。如下图,A依赖B,B依赖C,C依赖A:

    image-20210730200115421.png

    而Spring中循环依赖有三种:

    • 构造器注入
    • setter注入(单例,默认形式)
    • setter注入(原型,prototype)

    本文主要是对Spring对于这三种依赖分别是怎么处理的一次源码追溯记录。

    测试代码

    A,B,C三个类相互依赖

    public class A {
        private B b;
    
       public A() {
       }
    
       public A(B b) {
          this.b = b;
       }
    
       public B getB() {
          return b;
       }
    
       public void setB(B b) {
          this.b = b;
       }
    }
    
    public class B {
        private C c;
    
        public B() {
        }
    
        public B(C c) {
            this.c = c;
        }
    
        public void setC(C c) {
            this.c = c;
        }
    
        public C getC() {
            return c;
        }
    }
    
    public class C {
        private A a;
    
        public C() {
        }
    
        public C(A a) {
            this.a = a;
        }
    
        public A getA() {
            return a;
        }
    
        public void setA(A a) {
            this.a = a;
        }
    }
    

    测试代码:

    public class Main {
        public static void main(String[] args) {
            ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
            System.out.println(ac.getBean(A.class));
        }
    }
    

    构造器注入

    xml配置

    <bean id="A" class="A">
          <constructor-arg name="b" ref="B"/>
       </bean>
    
        <bean id="B" class="B" >
          <constructor-arg name="c" ref="C"/>
       </bean>
    
        <bean id="C" class="C" >
          <constructor-arg name="a" ref="A"/>
       </bean>
    

    直接运行,发现报错了,说明Spring无法解决构造器注入循环依赖。

    Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'A' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'B' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'B' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'C' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'C' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'A' while setting constructor argument; 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:342)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:113)
        at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:707)
        at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:198)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1354)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
        at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144)
        at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85)
        at Main.main(Main.java:6)
    ...
    

    通过报错

    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:342)

    定位到resolveReference()代码。

    @Nullable
    private Object resolveReference(Object argName, RuntimeBeanReference ref) {
       try {
          Object bean;
          // 获取依赖的Bean引用名称,如A依赖B,那么这里ref就是B
          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);
          }
          else {
             // 从当前容器中获取指定的bean引用
             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的地方,也就是触发依赖注入的地方
        @SuppressWarnings("unchecked")
        protected <T> T doGetBean(
                String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
                throws BeansException {
    
            String beanName = transformedBeanName(name);
            Object bean;
    
            // Eagerly check singleton cache for manually registered singletons.
            // 先从缓存中获取Bean,防止已经创建过的单例Bean重复创建
            Object sharedInstance = getSingleton(beanName);
            if (sharedInstance != null && args == null) {
                if (logger.isDebugEnabled()) {
                    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 + "'");
                    }
                }
                // 完成FactoryBean的相关处理
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
            }
    
            else {
                // Fail if we're already creating this bean instance:
                // We're assumably within a circular reference.
                // 检查当前bean是否正在创建中
                // 类型为protoType的bean循环依赖,这里会报错
                if (isPrototypeCurrentlyInCreation(beanName)) {
                    throw new BeanCurrentlyInCreationException(beanName);
                }
    
                // Check if bean definition exists in this factory.
                // 检查IOC容器中BeanDefinition是否存在,检查是否能从当前的BeanFactory中取得所需要的Bean。
                // 如果当前的工厂取不到,则委托父容器BeanFactory去寻找。
                // 如果父容器工厂也取不到,那么往上沿着继承关系继续委托父容器去寻找
                BeanFactory parentBeanFactory = getParentBeanFactory();
                // 如果父容器存在,并且当前容器中找不到指定bean的BeanDefinition
                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.
                        // 委托父容器根据bean的原始名称和参数去寻找
                        return (T) parentBeanFactory.getBean(nameToLookup, args);
                    }
                    else {
                        // No args -> delegate to standard getBean method.
                        // 委托父容器根据bean的原始名称和类型去寻找
                        return parentBeanFactory.getBean(nameToLookup, requiredType);
                    }
                }
    
                // 创建的bean是否需要进行类型检查
                if (!typeCheckOnly) {
                    // 标记bean已经被创建
                    markBeanAsCreated(beanName);
                }
    
                try {
                    // 根据bean名称获取BeanDefinition
                    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                    checkMergedBeanDefinition(mbd, beanName, args);
    
                    // Guarantee initialization of beans that the current bean depends on.
                    // 获取当前bean的所有依赖bean,这里会触发一个getBean的递归使用,
                    // 直到渠道一个没有任何依赖的Bean为止
                    String[] dependsOn = mbd.getDependsOn();
                    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 + "'");
                            }
                            registerDependentBean(dep, beanName);
                            try {
                                getBean(dep);
                            }
                            catch (NoSuchBeanDefinitionException ex) {
                                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                            }
                        }
                    }
    
                    // Create bean instance.
                    // 创建单例bean
                    if (mbd.isSingleton()) {
                        // 采用匿名内部类的方式,为依赖创建一个bean实例
                        // 构造器注入循环依赖时,循环一遍这里会报错
                        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);
                    }
    
                    // 创建prototype bean
                    else if (mbd.isPrototype()) {
                        // It's a prototype -> create a new instance.
                        Object prototypeInstance = null;
                        try {
                            // 在创建bean前会把beanName放入prototypesCurrentlyInCreation
                            beforePrototypeCreation(beanName);
                            prototypeInstance = createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                        bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                    }
    
                    // 创建除单例,原型以外的bean
                    else {
                        String scopeName = mbd.getScope();
                        if (!StringUtils.hasLength(scopeName)) {
                            throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
                        }
                        Scope scope = this.scopes.get(scopeName);
                        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 = 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进行类型检查,如果没有问题,则返回这个新创建的bean
            // 这里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;
        }
    

    通过DEBUG后发现进过一轮A,B,C依赖创建后再创建依赖A时getSingleton()报错

    if (mbd.isSingleton()) {
      // 采用匿名内部类的方式,为依赖创建一个bean实例
      sharedInstance = getSingleton(beanName, () -> {
        try {
          return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
          ...
        }
      });
      ...
    }
    

    再对getSingleton()进行DEBUG。

    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;
       }
    }
    

    发现beforeSingletonCreation(beanName);报错。

    /** Names of beans that are currently in creation */
        private final Set<String> singletonsCurrentlyInCreation =
                Collections.newSetFromMap(new ConcurrentHashMap<>(16));
                
     protected void beforeSingletonCreation(String beanName) {
            // 构造器注入在一轮循环后!this.singletonsCurrentlyInCreation.add(beanName)报错,
            // singletonsCurrentlyInCreation中bean已经存在了
            if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
        }
    

    singletonsCurrentlyInCreation是一个set集合,每次创建bean前,都会对当前的bean进行记录。

    循环依赖中:

    1. 创建A类bean,singletonsCurrentlyInCreation添加A,发现A类bean依赖B类bean,B类bean还没创建
    2. 于是去创建B类bean,singletonsCurrentlyInCreation添加B,发现B类bean依赖C类bean,C类bean还没创建
    3. 于是去创建C类bean,singletonsCurrentlyInCreation添加C,发现C类bean依赖A类bean,A类bean还没创建
    4. 于是继续去创建A类bean,当尝试往singletonsCurrentlyInCreation添加A的时候,因为第一步已经被添加了,表示正在被创建,所以添加失败,报错。

    setter注入(单例)

    xml配置:

    <bean id="A" class="A">
      <property name="b" ref="B"/>
    </bean>
    
    <bean id="B" class="B" >
      <property name="c" ref="C"/>
    </bean>
    
    <bean id="C" class="C" >
      <property name="a" ref="A"/>
    </bean>
    

    运行发现没有报错,说明Spring帮忙解决了setter注入(单例)的循环依赖。

    于是去探究和构造器注入有什么区别。

    因为主要方法还是那个doGetBean(),所以在那边DEBUG。

    发现一轮依赖后能从缓存中获取A类的bean了。

    // 先从缓存中获取Bean,防止已经创建过的单例Bean重复创建
    // setter注入(单例),一轮后sharedInstance不为null
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
      if (logger.isDebugEnabled()) {
        if (isSingletonCurrentlyInCreation(beanName)) {
          ...
        }
        else {
          ...
        }
      }
      ...
    }
    

    而在构造器注入中从缓存中获取beanObject sharedInstance = getSingleton(beanName)一直是null,所以走得都是else创建bean的逻辑。

    进入getSingleton()

    /** Cache of singleton objects: bean name --> bean instance */
        private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    /** Cache of early singleton objects: bean name --> bean instance */
        private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    
    /** Cache of singleton factories: bean name --> ObjectFactory */
        private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    @Nullable
    public Object getSingleton(String beanName) {
       return getSingleton(beanName, true);
    }
    
    @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) {
              // setter注入(单例),一轮后this.singletonFactories.get(beanName)能获取到创建bean的单例工厂
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
            return singletonObject;
        }
    

    这就是有名的Spring三级缓存,先从一级缓存singletonObject获取bean,如果没有,那么去earlySingletonObject获取早期bean(还没进行包装处理的bean)。如果还是没有,那么去singletonFactories中获取创建早期bean的单例工厂。

    而setter注入一轮后,singletonFactories 中可以获取到单例工厂,于是去寻找是何时往singletonFactories添加了A类的bean单例创建工厂。

    通过used查找,发现只有这一个方法往singletonFactories添加单例工厂。

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

    在往上查找发现是doCreatBean()中调用了这个方法,而这个方法在createBean()被调用。

    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
                throws BeanCreationException {
            ...
            try {
          // 这里调用了doCreateBean
                Object beanInstance = doCreateBean(beanName, mbdToUse, args);
                if (logger.isDebugEnabled()) {
                    logger.debug("Finished creating instance of bean '" + beanName + "'");
                }
                return beanInstance;
            }
            catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
                ...
            }
            catch (Throwable ex) {
                ...
            }
        }
    
    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @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);
       }
       Object bean = instanceWrapper.getWrappedInstance();
       Class<?> beanType = instanceWrapper.getWrappedClass();
       if (beanType != NullBean.class) {
          mbd.resolvedTargetType = beanType;
       }
    
       // Allow post-processors to modify the merged bean definition.
       ...
    
       // Eagerly cache singletons to be able to resolve circular references
       // even when triggered by lifecycle interfaces like BeanFactoryAware.
       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");
          }
          // 存放beanName对应的单例工厂,这里调用了addSingletonFactory
          addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
       }
    
       // Initialize the bean instance.
       ...
       // Register bean as disposable.
       ...
    
       return exposedObject;
    }
    

    createBean()doGetBean()中调用的地方经过构造器注入的追溯很清楚了。

    if (mbd.isSingleton()) {
      // 采用匿名内部类的方式,为依赖创建一个bean实例
      sharedInstance = getSingleton(beanName, () -> {
        try {
          return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
          ...
        }
      });
      ...
    }
    

    也就是说在第一轮尝试创建bean实例的时候通过createBean()singletonFactories添加了单例工厂。

    可是构造器注入也同样走的是这套代码,为什么构造器注入中没有往singletonFactories添加单例工厂呢?

    应该是中间哪一步断了,于是再对doCreateBean进行构造器注入的DEBUG,发现到instanceWrapper = createBeanInstance(beanName, mbd, args)抛出BeanCreationException异常。

    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
          throws BeanCreationException {
    
       ...
       if (instanceWrapper == null) {
         // 构造器注入这里抛出异常
          instanceWrapper = createBeanInstance(beanName, mbd, args);
       }
       ...
    
       // Eagerly cache singletons to be able to resolve circular references
       // even when triggered by lifecycle interfaces like BeanFactoryAware.
       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");
          }
          // 存放beanName对应的单例工厂,这里调用了addSingletonFactory
          addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
       }
    
       ...
    
       return exposedObject;
    }
    

    createBeanInstance():

    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
       // Make sure bean class is actually resolved at this point.
       Class<?> beanClass = resolveBeanClass(mbd, beanName);
    
       if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
          throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
       }
    
       Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
       if (instanceSupplier != null) {
          return obtainFromSupplier(instanceSupplier, beanName);
       }
    
       if (mbd.getFactoryMethodName() != null) {
          return instantiateUsingFactoryMethod(beanName, mbd, args);
       }
    
       // Shortcut when re-creating the same bean...
       boolean resolved = false;
       boolean autowireNecessary = false;
       if (args == null) {
          synchronized (mbd.constructorArgumentLock) {
             if (mbd.resolvedConstructorOrFactoryMethod != null) {
                resolved = true;
                autowireNecessary = mbd.constructorArgumentsResolved;
             }
          }
       }
       if (resolved) {
          if (autowireNecessary) {
             return autowireConstructor(beanName, mbd, null, null);
          }
          else {
             return instantiateBean(beanName, mbd);
          }
       }
    
       // Candidate constructors for autowiring?
       Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
       if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
             mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
          return autowireConstructor(beanName, mbd, ctors, args);
       }
    
       // No special handling: simply use no-arg constructor.
       // 调用无参构造函数创建bean实例
       return instantiateBean(beanName, mbd);
    }
    

    再进行DEBUG后发现最后一行return instantiateBean(beanName, mbd);抛出了错误。其实从注释很清楚,这是依靠无参构造方法来实例化Bean,而构造器注入要求的是有参数的构造方法,所以这里抛出错误也算情理之中。

    setter注入(原型)

    xml代码:

    <bean id="A" class="A" scope="prototype">
       <constructor-arg name="b" ref="B"/>
    </bean>
    
    <bean id="B" class="B" scope="prototype">
            <constructor-arg name="c" ref="C"/>
        </bean>
    
    <bean id="C" class="C" scope="prototype">
            <constructor-arg name="a" ref="A"/>
        </bean>
    

    还是去运行代码,发现报错了,Spring没法解决setter注入(原型)方式的循环依赖。

    于是再对doGetBean()DEBUG,发现这里抛出了错误。

    // 检查当前bean是否正在创建中
    if (isPrototypeCurrentlyInCreation(beanName)) {
       throw new BeanCurrentlyInCreationException(beanName);
    }
    
    private final ThreadLocal<Object> prototypesCurrentlyInCreation =
                new NamedThreadLocal<>("Prototype beans currently in creation");
    
    protected boolean isPrototypeCurrentlyInCreation(String beanName) {
            // 每次在创建bean前,先判断当前bean是否正在创建中
            Object curVal = this.prototypesCurrentlyInCreation.get();
            return (curVal != null &&
                    (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
        }
    

    在创建bean前判断当前bean是否正在被创建,虽然因为多线程用了ThreadLocal拿来记录当前正在创建的bean,但是应该和构造器注入判断出是循环依赖的原因一样。

    找到是beforePrototypeCreation()中往prototypesCurrentlyInCreation添加了当前的bean。

    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);
       }
    }
    

    beforePrototypeCreation()doGetBean()中调用位置。

    // 创建prototype bean
    else if (mbd.isPrototype()) {
       // It's a prototype -> create a new instance.
       Object prototypeInstance = null;
       try {
          // 这里,在创建bean前会把beanName放入prototypesCurrentlyInCreation
          beforePrototypeCreation(beanName);
          prototypeInstance = createBean(beanName, mbd, args);
       }
       finally {
          afterPrototypeCreation(beanName);
       }
       bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
    }
    

    总结

    1. Spring会先从三级缓存中获取bean,如果没有,再尝试创建bean。
    2. 创建有依赖的bean是一个递归过程,Spring会把每一个正在创建的bean名称放入set中,依靠判断set中是否存在判断是否存在循环依赖。
    3. 构造器注入和setter注入(单例)一个Spring能解决,一个不能解决的原因是,setter注入依靠无参构造方法可以创建一个bean实例,于是Spring把单例工厂放入第三级缓存。等一轮依赖过后就可以从第三级缓存中获取这个单例工厂,靠工厂可以创建bean。

    相关文章

      网友评论

          本文标题:追溯IOC循环依赖处理

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