学习spring对循环依赖的处理方式

1. 什么是循环依赖

简单的说就是Bean之间出现了依赖的闭环问题,例如Bean A依赖于Bean B,而Bean B也依赖于Bean A。若是不进行处理,那么在Bean A和Bean B的建立过程当中会出现对象建立死循环而没法正常的继续执行下一步的代码。逻辑示意图以下:java

2. 如何处理循环依赖

  • 核心思想:

    使用缓存将Bean的首次建立和二次建立进行逻辑区分spring

  • 大概思路:
  1. 建立一个全局缓存Cache,用于在首次实例化Bean时缓存建立的信息。
  2. 在一个Bean实例化前先读取缓存Cache信息,若没有对象缓存就进行首次建立,不然就是二次建立直接返回缓存对象。
  3. 当出现Bean之间的循环依赖时,一个Bean二次建立时会直接经过缓存Cache返回首次建立的对象,从而跳出循环建立的怪圈。
  • 从思路推断大概的代码逻辑:
  1. 建立SimpleBean对象(@8897),以后将其放入缓存中,再进行依赖FooBean对象的建立
  2. 建立FooBean对象(@2546),以后将其放入缓存中,再进行依赖SimpleBean对象的建立
  3. 二次建立SimpleBean对象时直接从缓存中获取SimpleBean对象(@8897),将对象(@8897)赋值到FooBean对象(@2546)中完成FooBean的建立
  4. 回到SimpleBean对象依赖FooBean对象的过程当中,将建立完成的FooBean对象(@2546)赋值到SimpleBean对象(@8897)中完成循环依赖
  • 代码演示

    net.plaz.bean.SimpleBean.java缓存

    public class SimpleBean {
    
       private FooBean fooBean;
    
       public SimpleBean(){
          System.out.println("SimpleBean构造");
       }
    
       public FooBean getFooBean() {
          return fooBean;
       }
    
       public void setFooBean(FooBean fooBean) {
          this.fooBean = fooBean;
       }
    }
    复制代码

    net.plaz.bean.FooBean.javaapp

    public class FooBean {
    
       private SimpleBean simpleBean;
    
       public FooBean(){
          System.out.println("FooBean构造");
       }
    
       public void setSimpleBean(SimpleBean simpleBean) {
          this.simpleBean = simpleBean;
       }
    
       public SimpleBean getSimpleBean() {
          return simpleBean;
       }
    }
    复制代码

    net.plaz.bean.SpringFactory.javaide

    public class SpringFactory {
    
       //bean缓存(<'net.plaz.bean.FooBean', new FooBean()>)
       private Map<String, Object> earlySingletonCacheMap = new HashMap<String, Object> ();
    
       //使用needWiredMap模拟实例化对象的须要注入属性
       //<'net.plaz.bean.FooBean', 'net.plaz.bean.SimpleBean'>表示FooBean须要注入一个SimpleBean
       private Map<String, String> needWiredMap = new HashMap<String, String> ();
    
    
       public Object getBeanInstance(String className){
          //先从缓存中获取,若获取到,则直接返回
          Object obj = earlySingletonCacheMap.get(className);
          if(obj == null){
             //缓存中没有对象,则要新建立一个对象,并进行属性赋值
             try {
    
                Class<?> clazz = Class.forName(className);
                obj = clazz.newInstance();
                //将建立的对象加入缓存中
                earlySingletonCacheMap.put(className, obj);
                //以后进行属性赋值
                //获取要注入的val(递归建立对象)
                String propertyStr = needWiredMap.get(className);
                Object propertyObj = getBeanInstance(propertyStr);
                //key
                String propertyKey = getPropertyName(propertyStr);
                //将val经过set方法对po的属性赋值
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(propertyKey, obj.getClass());
                propertyDescriptor.getWriteMethod().invoke(obj, propertyObj);
    
             } catch (ClassNotFoundException e) {
                e.printStackTrace();
             } catch (IllegalAccessException e) {
                e.printStackTrace();
             } catch (InstantiationException e) {
                e.printStackTrace();
             } catch (IntrospectionException e) {
                e.printStackTrace();
             } catch (InvocationTargetException e) {
                e.printStackTrace();
             }
          }
    
          return obj;
       }
    
       /** * 功能描述: 获取属性名称 * <br> * @Param: [className] * @Return: java.lang.String */
       private String getPropertyName(String className){
          String[] strArr = className.split("\\.");
          String prop = strArr[strArr.length -1];
          char[] chars = prop.toCharArray();
          chars[0] += 32;
          return String.valueOf(chars);
       }
    
       //下面是set/get方法
       public Map<String, Object> getEarlySingletonCacheMap() {
          return earlySingletonCacheMap;
       }
    
       public void setEarlySingletonCacheMap(Map<String, Object> earlysingletonCacheMap) {
          this.earlySingletonCacheMap = earlysingletonCacheMap;
       }
    
    
       public Map<String, String> getNeedWiredMap() {
          return needWiredMap;
       }
    
       public void setNeedWiredMap(Map<String, String> needWiredMap) {
          this.needWiredMap = needWiredMap;
       }
    }
    复制代码

    代码调用函数

    public static void main(String[] args) {
       //要注入的属性(模拟bean依赖关系)
       //<'net.plaz.bean.FooBean', 'net.plaz.bean.SimpleBean'>表示FooBean须要注入一个SimpleBean
       Map<String, String> needWiredMap = new HashMap<String, String> ();
       needWiredMap.put("net.plaz.bean.SimpleBean", "net.plaz.bean.FooBean");
       needWiredMap.put("net.plaz.bean.FooBean", "net.plaz.bean.SimpleBean");
    
       //建立bean工厂
       SpringFactory factory = new SpringFactory();
       //设置bean关系
       factory.getNeedWiredMap().putAll(needWiredMap);
    
       //模拟要实例化的列表
       List<String> beanList = new ArrayList<>();
       beanList.add("net.plaz.bean.SimpleBean");
       beanList.add("net.plaz.bean.FooBean");
    
       List<Object> result = new ArrayList<> ();
       for(String bean : beanList){
          result.add(factory.getBeanInstance(bean));
       }
       System.out.println(result);
    }
    
    //执行结果
    //成功的处理了循环依赖(●'◡'●)
    SimpleBean构造
    FooBean构造
    [net.plaz.bean.SimpleBean@2344fc66, net.plaz.bean.FooBean@458ad742]
    复制代码

代码执行逻辑以下:学习

  1. 先建立simpleBean空对象,将对象放入未实例化完成的缓存中便于其余对象调用ui

  2. 以后对其依赖对象FooBean进行实例化,一样将FooBean空对象放入未实例化完成的缓存中this

  3. 在FooBean实例化中进行填充对象属性时,又要建立simpleBean对象;此时simpleBean处于建立中的bean列表中,同时在未实例化完成的缓存中已经有一个simpleBean对象,因此直接将步骤1建立的simpleBean对象引用赋值到FooBean对象的属性中lua

  4. 在FooBean建立完成后回到步骤2,将建立完成的FooBean对象引用赋值给simpleBean对象完成实例化

3. spring如何处理循环依赖

在spring DI过程当中,对于循环依赖的处理方式和上述处理基本相同(上述处理属于spring的简化版)。下面来扒下spring的源码学习下大体的流程:

1.咱们一般使用getBean(java.lang.Class<T>)从IOC中获取bean信息,实际上在IOC容器经过扫描包或加载XML后也会循环调用getBean(...)进行Bean的首轮实例化(有兴趣详见DefaultListableBeanFactory#preInstantiateSingletons())。下面来详细了解下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)先获取缓存对象:

//DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //从singletonObjects(存储已完成实例化的单例对象)缓存中获取
   Object singletonObject = this.singletonObjects.get(beanName);
    //没有获取到bean,判断当前beanName对象是否在建立中
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
         //从earlySingletonObjects(存储刚实例化的单例对象)缓存中获取==>循环依赖的一种缓存对象
         singletonObject = this.earlySingletonObjects.get(beanName);
         //没有获取到bean,判断是否容许提早引用其余bean
         if (singletonObject == null && allowEarlyReference) {
             //从singletonFactories缓存中获取==>循环依赖的一种缓存对象
             //获取到的对象是一个函数式接口对象,能直接获取到beanName首次建立的对象
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
                //singletonFactories有,earlySingletonObjects没有,则将对象从singletonFactories挪到earlySingletonObjects
               singletonObject = singletonFactory.getObject();
               this.earlySingletonObjects.put(beanName, singletonObject);
               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(...)的主要逻辑有如下几步:

  1. 建立一个bean的包装对象instanceWrapper(实际为Class.forName(className).newInstance()建立,有兴趣自行可跟踪代码)
  2. 经过addSingletonFactory(...)将刚实例化的对象放入缓存中
  3. populateBean(...)中处理bean对象的依赖属性(在这里递归调用其余依赖的bean)
  4. 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);
   }
}
复制代码

主要逻辑是以下:

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

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的属性赋值过程。

4. spring中循环依赖的的一些思考

上述所spring处理的循环依赖有两个关键点:

  1. 建立的bean是单例模式的,

  2. 是经过属性的set方法对其依赖bean进行赋值的。

因此思考如下几个问题:

  1. Q:若bean类型是原型模式(prototye)是否可适用这样的循环依赖处理方式?

    S:不能够,原型模式每次建立的bean都是新对象,没法保证对象统一,spring也不会缓存原型模式的bean

  2. Q:处理循环依赖中,是否能够经过构造方法进行依赖注入?

    S:不能够,set方法是在对象建立后进行的,而构造方法是在对象建立中执行的,此时beanA是没有缓存信息的,只能在建立以前就获取到依赖的beanB对象,而依赖的beanB再次嵌套建立beanA时,因为beanA处于建立中的状态,就会报错。

  3. Q:除了经过set方法进行依赖注入外,还能够经过其余方法完成依赖注入吗?

    S:只要是在对象建立以后进行bean赋值基本均可实现,下面介绍几种:

    • 使用@Autowired和@PostConstruct在初始化方法中进行赋值

    • 实现ApplicationContextAware和InitializingBean接口在afterPropertiesSet()方法中调用getBean()赋值

    • 使用@Lazy注解,先注入代理对象,在后面首次使用时完成赋值

相关文章
相关标签/搜索