源码解读系列-spring源码(十一)- 循环依赖

8. Bean-循环依赖

什么是循环依赖?

循环依赖:一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用(闭环)。

例:第一种情况:两个对象之间的直接依赖:

 第二种情况:多个对象之间的间接依赖

注意,这里不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,除非有终结条件。

怎么检测是否存在循环依赖

检测循环依赖相对比较容易,Bean在创建的时候可以给该Bean打标记,如果递归调用回来发现正在创建中的话,即说明了循环依赖了。

循环依赖_效果演示

Spring中循环依赖场景有:

  • 构造器的循环依赖
  • field属性的循环依赖
(1)构造器的循环依赖

构造器的循环依赖就是在构造器中有属性循环依赖,如下:

public class testService1 {

    private testService2 service_2;

    public testService1(testService2 service_2) {
        this.service_2 = service_2;
    }

    public void aTest(){
        System.out.println("testService1,注入了属性" + service_2 );
    }
}
public class testService2 {

    private testService1 service_1;

    public testService2(testService1 service_1) {
        this.service_1 = service_1;
    }

    public void bTest(){
        System.out.println("testService2,注入了属性" + service_1 );
    }
    
}
applicationContext.xml

<!--testService1通过构造方法注入testService2-->
<bean id="testService1" class="com.itheima.cyclic.testService1">
   <constructor-arg  name="service_2" ref="testService2"/>
</bean>

<!--testService2通过构造方法注入testService1-->
<bean id="testService2" class="com.itheima.cyclic.testService2">
   <constructor-arg name="service_1" ref="testService1"/>
</bean>
测试代码:

/**
     * 循环依赖效果演示
     * @param args
     */
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("classpath:applicationContext-cyclic.xml");

        testService1 testService1 = (com.itheima.cyclic.testService1) applicationContext.getBean("testService1");
        testService1.aTest();
    }

测试结果:产生了循环依赖

(2)field属性的循环依赖
public class TestService1 {

    private TestService2 testService2;

    public void setTestService2(TestService2 testService2) {
        this.testService2 = testService2;
    }

    public void aTest(){
        System.out.println("testService1,注入了" + testService2 );
    }

}
public class TestService2 {

    private TestService1 testService1;

    public void setTestService1(TestService1 testService1) {
        this.testService1 = testService1;
    }

    public void aTest(){
        System.out.println("testService1,注入了属性" + testService1 );
    }
}
applicationContext.xml

    <bean id="testService1" class="com.itheima.cyclic.TestService1">
        <property name="testService2" ref="testService2"/>
    </bean>

    <bean id="testService2" class="com.itheima.cyclic.TestService2">
        <property name="testService1" ref="testService1" />
    </bean>
测试结果:

结论:

构造器注入引起的循环依赖(不能解决

单例Bean的Setter注入产生的循环依赖(能解决

Spring怎么解决循环依赖

Spring的循环依赖的理论依据其实是基于Java的引用传递,当我们获取到对象的引用时,对象的field或则属性是可以延后设置的(但是构造器必须是在获取引用之前)。

三级缓存

Spring为了解决单例的循环依赖问题,使用了三级缓存(三个map)

/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

 

 Spring解决循环依赖流程图

 

循环依赖_源码剖析

1.使用getBean(java.lang.Class)从IOC中获取bean信息,实际上在IOC容器通过扫描包或加载XML后也会循环调用getBean(...)进行Bean的首轮实例化。

下面来详细了解下getBean(...)中对于循环依赖的处理:

//org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
//doGetBean是getBean方法的实际逻辑方法,这里只贴出了相关的部分代码
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
   //处理bean名称的规范问题
   final String beanName = transformedBeanName(name);
   Object bean;

   // Eagerly check singleton cache for manually registered singletons.
   //从缓存中获取bean实例
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
      if (logger.isTraceEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

   else {
      //省略...

      try {
         //获取beanName对应的BeanDefinition
         final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
        //省略...
          
         // Create bean instance.
         //根据bean的作用域来创建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.
                  destroySingleton(beanName);
                  throw ex;
               }
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }
         else if (mbd.isPrototype()) {
            //创建原型模式bean
            // It's a prototype -> create a new instance.
            Object prototypeInstance = null;
            try {
               beforePrototypeCreation(beanName);
               prototypeInstance = createBean(beanName, mbd, args);
            }
            finally {
               afterPrototypeCreation(beanName);
            }
            bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
         }else {
             //创建其他模式bean
            String scopeName = mbd.getScope();
            final 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;
      }
   }

   //省略...
   return (T) bean;
}

上述doGetBean大致做了几个步骤:

  • 1.尝试根据beanName从缓存中获取获取bean对象

  • 2.若获取到缓存对象则执行getObjectForBeanInstance(...)后返回bean信息

  • 3.若没有获取到缓存对象(首次创建),则根据bean的作用域类型来采取不同方式创建bean(这里默认为单例模式),然后再执行getObjectForBeanInstance(...)后返回bean信息

其中涉及到循环依赖的处理有getSingleton(beanName)先获取缓存对象:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // Quick check for existing instance without full singleton lock

        // 1.从单例对象缓存(1级缓存)中获取beanName对应的单例对象
        Object singletonObject = this.singletonObjects.get(beanName);
        // 2.如果单例对象缓存(1级缓存)中没有,并且该beanName对应的单例bean正在创建中
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            // 3.从早期单例对象缓存中(二级缓存)获取单例对象(之所称成为早期单例对象,是因为earlySingletonObjects里
            // 的对象的都是通过提前曝光的ObjectFactory创建出来的,还未进行属性填充等操作)
            singletonObject = this.earlySingletonObjects.get(beanName);

            // 4.如果在早期单例对象缓存中(二级缓存)也没有,并且允许创建早期单例对象引用
            if (singletonObject == null && allowEarlyReference) {
                synchronized (this.singletonObjects) {
                    // Consistent creation of early reference within full singleton lock
                    // 6.从单例工厂缓存中(三级缓存)获取beanName的单例工厂
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        // 再次从二级缓存中获取,重复校验
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        // 为null
                        if (singletonObject == null) {
                            // 6.再从单例工厂缓存中(三级缓存)获取beanName的单例工厂
                            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                // 7.如果存在单例对象工厂,则通过工厂创建一个单例对象
                                singletonObject = singletonFactory.getObject();
                                // 8.将通过单例对象工厂创建的单例对象,放到早期单例对象缓存中
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                // 9.移除该beanName对应的单例对象工厂,因为该单例工厂已经创建了一个实例对象,并且放到earlySingletonObjects缓存了,
                                // 因此,后续获取beanName的单例对象,可以通过earlySingletonObjects缓存拿到,不需要在用到该单例工厂
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }
        return singletonObject;
    }

分别从一级缓存、二级缓存、三级缓存中进行查找

2.这里我们的bean按照单例模式,走首次创建路径createBean(beanName, mbd, args);,而createBean(beanName, mbd, args);中真正的逻辑方法是doCreateBean(...),下面我们看下doCreateBean(...)的方法:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      //根据beanName将当前对象从未完成实例化列表缓存中移除并返回
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   //若未完成实例化列表缓存中没有数据则创建一个空对象
   if (instanceWrapper == null) {
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   final Object bean = instanceWrapper.getWrappedInstance();
   //省略...

   // Eagerly cache singletons to be able to resolve circular references
   // even when triggered by lifecycle interfaces like BeanFactoryAware.
   //将bean写入提前暴露的缓存中(此时的bean刚实例化,还没有对其属性进行赋值处理)
   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");
      }
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
      //将beandefinition中的属性写入对应的instanceWrapper对象实例中
      //依赖循环就是在这里处理的 
      populateBean(beanName, mbd, instanceWrapper);
      //如果exposedObject对象有实现一些aware、init接口则初始化这些接口
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   catch (Throwable ex) {
      if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
         throw (BeanCreationException) ex;
      }
      else {
         throw new BeanCreationException(
               mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
      }
   }
   //省略...

   return exposedObject;
}

doCreateBean(...)的主要逻辑有以下几步:

  • 创建一个bean的包装对象instanceWrapper(实际为Class.forName(className).newInstance()创建,有兴趣自行可跟踪代码)
  • 通过addSingletonFactory(...)将刚实例化的对象放入缓存中
  • 在populateBean(...)中处理bean对象的依赖属性(在这里递归调用其他依赖的bean)
  • 在initializeBean(...)中调用对象的一些初始化接口(如实现InitializingBean),并返回结果bean

涉及循环依赖的处理有addSingletonFactory(...)和populateBean(...)两部分,我们先看下addSingletonFactory(...)将bean加入缓存中:

//org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(singletonFactory, "Singleton factory must not be null");
   synchronized (this.singletonObjects) {
      //没有创建过beanName的bean则加入缓存
      if (!this.singletonObjects.containsKey(beanName)) {
         //存储在singletonFactories中,在getSingleton(...)中获取调用
         this.singletonFactories.put(beanName, singletonFactory);
         this.earlySingletonObjects.remove(beanName);
         this.registeredSingletons.add(beanName);
      }
   }
}

//参数ObjectFactory<?> singletonFactory是一个函数式接口对象
//内容为() -> getEarlyBeanReference(beanName, mbd, bean)
//调用singletonFactory会执行getEarlyBeanReference(beanName, mbd, bean),返回bean的首次创建对象
//实际上会在获取缓存对象的getSingleton(...)中调用 singletonFactory.getObject();
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    //忽略...
    return exposedObject;
}

 3.而populateBean(...)是根据BeanDefinition将属性赋值到刚创建的对象中,主要的逻辑在applyPropertyValues(...)中执行,大致代码如下:

//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyPropertyValues
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
   //省略...
    //创建属性解析器(主要完成属性值的处理,包括依赖其他bean的创建)
   BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

   // Create a deep copy, resolving any references for values.
   List<PropertyValue> deepCopy = new ArrayList<>(original.size());
   boolean resolveNecessary = false;
   for (PropertyValue pv : original) {
      if (pv.isConverted()) {
         deepCopy.add(pv);
      }
      else {
         //获取属性名称
         String propertyName = pv.getName();
         Object originalValue = pv.getValue();
         //使用解析器解析不同类型的值
         Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
          //将值包装到deepCopy的list中
         Object convertedValue = resolvedValue;
         boolean convertible = bw.isWritableProperty(propertyName) &&
               !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
         if (convertible) {
            convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
         }
         // Possibly store converted value in merged bean definition,
         // in order to avoid re-conversion for every created bean instance.
         if (resolvedValue == originalValue) {
            if (convertible) {
               pv.setConvertedValue(convertedValue);
            }
            deepCopy.add(pv);
         }
         else if (convertible && originalValue instanceof TypedStringValue &&
               !((TypedStringValue) originalValue).isDynamic() &&
               !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
            pv.setConvertedValue(convertedValue);
            deepCopy.add(pv);
         }
         else {
            resolveNecessary = true;
            deepCopy.add(new PropertyValue(pv, convertedValue));
         }
      }
   }
   if (mpvs != null && !resolveNecessary) {
      mpvs.setConverted();
   }

   // Set our (possibly massaged) deep copy.
   try {
       //将属性赋值到对象中
      bw.setPropertyValues(new MutablePropertyValues(deepCopy));
   }
   catch (BeansException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Error setting property values", ex);
   }
}

主要逻辑是如下:

  • 创建属性解析器valueResolver, 之后循环BeanDefinition中的属性列表,使用解析器对每个property进行实际值的解析(保存创建依赖bean对象)
  • 根据属性的名称将属性值赋值到对象中

4.涉及到循环依赖的逻辑是valueResolver.resolveValueIfNecessary(pv, originalValue),使用属性解析器获取property的实际内容,下面我们看下如何解析property的(只看依赖其他bean的property):

//org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveValueIfNecessary
@Nullable
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
   // We must check each value to see whether it requires a runtime reference
   // to another bean to be resolved.
    //处理依赖其他bean的property
   if (value instanceof RuntimeBeanReference) {
      RuntimeBeanReference ref = (RuntimeBeanReference) value;
      return resolveReference(argName, ref);
   }
    //省略...
}

//详细处理逻辑
@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);
        }
        else {
            //嵌套调用IOC容器的getBean方法
            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);
    }
}

上述逻辑比较清晰简单,就是根据依赖的beanName嵌套调用this.beanFactory.getBean(refName)去创建所依赖对象,创建完成后返回该bean信息。

总结: 到这里我们就可以知道spring是如何处理依赖循环的了:

(1)调用getBean(...)方法创建一个bean,前先从缓存getSingleton(...)中获取对象信息

(2)若是没有缓存,则首次创建后将其对象加入到三级缓存

(3)之后对创建的对象进行属性填充populateBean(...),填充过程中创建属性解析器对bean的属性进行处理

(4)若属性类型依赖其他的bean,则会嵌套调用IOC容器的getBean方法去创建所依赖的bean对象,直到出现从缓存中获取到对象后跳出嵌套逻辑,才可以完成整个bean的属性赋值过程。

循环依赖经典面试题

【Spring 为何需要三级缓存解决循环依赖,而不是二级缓存?】

答:只要两个缓存确实可以做到解决循环依赖的问题,但是有一个前提这个bean没被AOP进行切面代理,如果这个bean被AOP进行了切面代理,那么只使用两个缓存是无法解决问题

【三级缓存中为什么要添加ObjectFactory对象,而不是直接保存实例对象?】

答:因为假如想对添加到三级缓存中的实例对象进行增强,直接用实例对象是行不通的。

【构造器注入注入的循环依赖为什么无法解决?】

 答:源码中对于解决逻辑的第一句话:“我们先用构造函数创建一个 “不完整” 的 bean 实例”,从这句话可以看出,构造器循环依赖是无法解决的,因为当构造器出现循环依赖,我们连 “不完整” 的 bean 实例都构建不出来。

  • 21
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
dynamic-datasource-spring-boot-starter 是一个基于 Spring Boot 的动态数据库。它提供了在运行时动态切换数据、动态创建数据以及多租户的支持。 对于 dynamic-datasource-spring-boot-starter 的码分析,可以从以下几个方面进行思考: 1. 数据的自动装配:码中可能会使用 Spring Boot 的自动配置功能,通过扫描配置类或者注解的方式,自动将数据相关的类和 bean 注入到应用程序中。 2. 动态切换数据的实现:动态切换数据是 dynamic-datasource-spring-boot-starter 的核心功能之一。码中可能会涉及到 AOP、动态代理等技术,通过拦截器或者代理的方式,在运行时根据特定条件切换数据。 3. 动态创建数据的实现:动态-datasource-spring-boot-starter 提供了在运行时动态创建数据的功能。码中可能会包含一些工厂类或者构建者模式的实现,根据配置信息动态创建数据实例。 4. 多租户支持的实现:多租户是指一个系统可以同时服务于多个不同的客户或租户。dynamic-datasource-spring-boot-starter 也提供了对多租户的支持。码中可能会包含一些多租户相关的类和逻辑,如解析请求或者从配置文件中获取租户信息等。 总结来说,dynamic-datasource-spring-boot-starter 码分析涉及到数据的自动装配、动态切换数据的实现、动态创建数据和多租户支持等方面的内容。可以通过阅读码来深入了解其实现原理和技术细节,从而更好地使用和定制该库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值