Spring循环依赖

308 阅读9分钟

写在前

本篇通过代码方式来展示Spring循环依赖的出现过程,以及是如何解决循环依赖,最后再通过源码来看看Spring中具体是如何解决的。

代码示例

其他前置代码

public interface IApi {

   void say();
}

//实例A
@Component
public class InstanceA implements IApi {

    @Autowired
    private InstanceB instanceB;

    public InstanceB getInstanceB() {
        return instanceB;
    }

    public void setInstanceB(InstanceB instanceB) {
        this.instanceB = instanceB;
    }

    public InstanceA(InstanceB instanceB) {
        this.instanceB = instanceB;
    }


    public InstanceA() {
        System.out.println("InstanceA实例化");
    }

   @Override
   public void say() {
      System.out.println("I'm A");
   }
}

//实例B
@Component
public class InstanceB  {


    @Autowired
    private InstanceA instanceA;


    public InstanceA getInstanceA() {
        return instanceA;
    }


    public void setInstanceA(InstanceA instanceA) {
        this.instanceA = instanceA;
    }

    public InstanceB(InstanceA instanceA) {
        this.instanceA = instanceA;
    }


    public InstanceB() {
        System.out.println("InstanceB实例化");
    }

}

//动态代理
public class JdkDynimcProxy implements InvocationHandler {

   private Object target;

   public JdkDynimcProxy(Object target) {
      this.target = target;
   }

   public Object getProxy() {
      return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
   }

   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.println("测试");
      return method.invoke(target,args);
   }
}


public class JdkProxyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {

   public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {

      // 假设B 被切点命中 需要创建代理
      if(bean instanceof InstanceA/*判断是不是被增强的类,是不是需要创建动态代理*/) {
         JdkDynimcProxy jdkDynimcProxy = new JdkDynimcProxy(bean);
         return  jdkDynimcProxy.getProxy();
      }
      return bean;
   }
}

@FunctionalInterface
public interface ObjectFactory<T> {

    /**
     * Return an instance (possibly shared or independent)
     * of the object managed by this factory.
     * @return the resulting instance
     * @throws BeansException in case of creation errors
     */
    T getObject() throws BeansException;

}

什么是循环依赖

image.png 如图所示:即在A类中注入了B的实例,在B中也注入了A的实例,那么Spring在创建这些实例的时候,就会出现循环依赖。

引入一级缓存

public class MainTest {
	private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();


	public static void loadBeanDefinition(){
		RootBeanDefinition aRootBeanDefinition = new RootBeanDefinition(InstanceA.class);
		RootBeanDefinition bRootBeanDefinition = new RootBeanDefinition(InstanceB.class);
		beanDefinitionMap.put("instanceA",aRootBeanDefinition);
		beanDefinitionMap.put("instanceB",bRootBeanDefinition);
	}

	public static void main(String[] args) throws InstantiationException, IllegalAccessException {
		loadBeanDefinition();
		for (String key : beanDefinitionMap.keySet()) {

			getBean(key);
		}
		InstanceA instanceA = (InstanceA) getBean("instanceA");
		instanceA.say();
	}

	// 一级缓存
	public static Map<String,Object> singletonObjects = new ConcurrentHashMap<>();

	public static Object getBean(String beanName) throws IllegalAccessException, InstantiationException {

		Object singleton = getSingleton(beanName);
		if (singleton != null){
			return singleton;
		}

		// 实例化
		RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
		Class<?> beanClass = beanDefinition.getBeanClass();
		Object instanceBean = beanClass.newInstance();

		// 添加一级缓存
		singletonObjects.put(beanName,instanceBean);

		// 属性赋值
		Field[] declaredFields = beanClass.getDeclaredFields();
		for (Field declaredField : declaredFields) {

			Annotation annotation = declaredField.getAnnotation(Autowired.class);
			if (annotation != null){
				String name = declaredField.getName();
				declaredField.setAccessible(true);
				declaredField.set(instanceBean,getBean(name));
			}

		}
		// 初始化


		return instanceBean;
	}

	public static Object getSingleton(String beanName){
		if (singletonObjects.containsKey(beanName)){
			return singletonObjects.get(beanName);
		}
		return null;
	}

}

引入一级缓存,似乎是可以解决了循环依赖如下图所示。但是,试想一下,如果在多线程情况下,就会出现问题,会获取到不完整的Bean。首先想到的是可以加锁,加锁确实是可以解决多线程下循环依赖的问题,但是不利于扩展,Spring当然不会这么干。 image.png

引入二级缓存

//================二级缓存
public class MainTest {
	private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();


	public static void loadBeanDefinition(){
		RootBeanDefinition aRootBeanDefinition = new RootBeanDefinition(InstanceA.class);
		RootBeanDefinition bRootBeanDefinition = new RootBeanDefinition(InstanceB.class);
		beanDefinitionMap.put("instanceA",aRootBeanDefinition);
		beanDefinitionMap.put("instanceB",bRootBeanDefinition);
	}

	public static void main(String[] args) throws InstantiationException, IllegalAccessException {
		loadBeanDefinition();
		for (String key : beanDefinitionMap.keySet()) {

			getBean(key);
		}
		InstanceA instanceA = (InstanceA) getBean("instanceA");
		instanceA.say();
	}

	// 一级缓存
	public static Map<String,Object> singletonObjects = new ConcurrentHashMap<>();

	// 二级缓存 为了将成熟bean和不完整的bean分开
	public static Map<String,Object> earlySingletonObjects = new ConcurrentHashMap<>();

	public static Object getBean(String beanName) throws IllegalAccessException, InstantiationException {

		Object singleton = getSingleton(beanName);
		if (singleton != null){
			return singleton;
		}

		// 实例化
		RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
		Class<?> beanClass = beanDefinition.getBeanClass();
		Object instanceBean = beanClass.newInstance();

		// 添加二级缓存
		earlySingletonObjects.put(beanName,instanceBean);

		// 属性赋值
		Field[] declaredFields = beanClass.getDeclaredFields();
		for (Field declaredField : declaredFields) {

			Annotation annotation = declaredField.getAnnotation(Autowired.class);
			if (annotation != null){
				String name = declaredField.getName();
				declaredField.setAccessible(true);
				declaredField.set(instanceBean,getBean(name));
			}

		}
		// 初始化

		// 添加一级缓存
		singletonObjects.put(beanName,instanceBean);

		return instanceBean;
	}

	public static Object getSingleton(String beanName){
		if (singletonObjects.containsKey(beanName)){
			return singletonObjects.get(beanName);
		}else if (earlySingletonObjects.containsKey(beanName)){
			return earlySingletonObjects.get(beanName);
		}
		return null;
	}

}

引入了二级缓存,首先从一级缓存中获取完整的Bean直接返回,获取不到再去二级缓存中获取不完整的Bean,进行属性赋值操作后再放入到一级缓存中。使用二级缓存解决了耦合、以及循环依赖问题。那么为什么还要引入三级缓存? image.png

引入三级缓存

public class MainTest {
   private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();


   public static void loadBeanDefinition(){
      RootBeanDefinition aRootBeanDefinition = new RootBeanDefinition(InstanceA.class);
      RootBeanDefinition bRootBeanDefinition = new RootBeanDefinition(InstanceB.class);
      beanDefinitionMap.put("instanceA",aRootBeanDefinition);
      beanDefinitionMap.put("instanceB",bRootBeanDefinition);
   }

   public static void main(String[] args) throws InstantiationException, IllegalAccessException {
      loadBeanDefinition();
      for (String key : beanDefinitionMap.keySet()) {

         getBean(key);
      }
      InstanceA instanceA = (InstanceA) getBean("instanceA");
      instanceA.say();
   }

   // 一级缓存
   public static Map<String,Object> singletonObjects = new ConcurrentHashMap<>();

   // 二级缓存 为了将成熟bean和不完整的bean分开
   public static Map<String,Object> earlySingletonObjects = new ConcurrentHashMap<>();

   // 三级缓存
   public static Map<String,ObjectFactory> singletonFactories = new ConcurrentHashMap<>();

   // 循环依赖标识
   public static Set<String> singletonCurrennlyInCreation = new HashSet<>();

   public static Object getBean(String beanName) throws IllegalAccessException, InstantiationException {

      Object singleton = getSingleton(beanName);
      if (singleton != null){
         return singleton;
      }

      // 正在创建
      if (!singletonCurrennlyInCreation.contains(beanName)){
         singletonCurrennlyInCreation.add(beanName);
      }

      // 实例化
      RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
      Class<?> beanClass = beanDefinition.getBeanClass();
      Object instanceBean = beanClass.newInstance();

      // 创建动态代理
      // 只在循环依赖的情况下,在实例化后创建动态代理
      // 那么需要判断是不是循环依赖
      singletonFactories.put(beanName, () -> new JdkProxyBeanPostProcessor().getEarlyBeanReference(earlySingletonObjects.get(beanName),beanName));

      // 添加二级缓存
      earlySingletonObjects.put(beanName,instanceBean);

      // 属性赋值
      Field[] declaredFields = beanClass.getDeclaredFields();
      for (Field declaredField : declaredFields) {

         Annotation annotation = declaredField.getAnnotation(Autowired.class);
         if (annotation != null){
            String name = declaredField.getName();
            declaredField.setAccessible(true);
            declaredField.set(instanceBean,getBean(name));
         }

      }
      // 初始化
      // 正常是初始化之后在创建动态代理

      if (earlySingletonObjects.containsKey(beanName)){
         instanceBean = getSingleton(beanName);
      }
      // 添加一级缓存
      singletonObjects.put(beanName,instanceBean);

      return instanceBean;
   }

   public static Object getSingleton(String beanName){
      Object bean = singletonObjects.get(beanName);

      // 说明是循环依赖
      if (bean == null && singletonCurrennlyInCreation.contains(beanName)){
         // 从二级缓存拿
         bean = earlySingletonObjects.get(beanName);
         if (bean == null){
            // 从三级缓存拿
            ObjectFactory objectFactory = singletonFactories.get(beanName);
            if (objectFactory != null){
               bean = objectFactory.getObject();
               earlySingletonObjects.put(beanName,bean);
            }
         }
      }

      return bean;
   }

}

例如出现这么一种情况,Bean A的aop动态代理创建时在初始化之后,但是循环依赖的Bean如果使用了AOP。 那无法等到解决完循环依赖再创建动态代理,因为这个时候已经注入属性,所以如果循环依赖的Bean使用了aop。需要提前创建aop。 杂七杂八.png

小结

如果只有一级缓存解决循环依赖也是可以的,只不过性能最低、耦合性、扩展性最差。如果只有二级缓存解决循环依赖也是可以的,耦合性太高。所以三级缓存解决循环依赖和处理多线程读取不完整Bean是最完美的方案。 image.png

源码

前面的流程在前面的文章中已有分析过,getBean()才是真正的取创建Bean实例,getBean()里面又去调用了doGetBean()方法,因此这里就从doGetBean()方法开始。

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

   /**
    * 在这里 传入进来的name 可能是 别名, 也有可能是工厂bean的name,所以在这里需要转换
    */
   final String beanName = transformedBeanName(name);
   Object 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 + "'");
         }
      }
      /**
       * /*
       * 如果 sharedInstance 是普通的单例 bean,下面的方法会直接返回。但如果
       * sharedInstance 是 FactoryBean 类型的,则需调用 getObject 工厂方法获取真正的
       * bean 实例。如果用户想获取 FactoryBean 本身,这里也不会做特别的处理,直接返回
       * 即可。毕竟 FactoryBean 的实现类本身也是一种 bean,只不过具有一点特殊的功能而已。
       */
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

   else {

      /**
       * spring 只能解决单例对象的setter 注入的循环依赖,不能解决构造器注入
       */
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }

      /**
       * 判断AbstractBeanFacotry工厂是否有父工厂(一般情况下是没有父工厂因为abstractBeanFactory直接是抽象类,不存在父工厂)
       * 一般情况下,只有Spring 和SpringMvc整合的时才会有父子容器的概念,
       * 比如我们的Controller中注入Service的时候,发现我们依赖的是一个引用对象,那么他就会调用getBean去把service找出来
       * 但是当前所在的容器是web子容器,那么就会在这里的 先去父容器找
       */
      BeanFactory parentBeanFactory = getParentBeanFactory();
      //若存在父工厂,切当前的bean工厂不存在当前的bean定义,那么bean定义是存在于父beanFacotry中
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
         //获取bean的原始名称
         String nameToLookup = originalBeanName(name);
         //若为 AbstractBeanFactory 类型,委托父类处理
         if (parentBeanFactory instanceof AbstractBeanFactory) {
            return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                  nameToLookup, requiredType, args, typeCheckOnly);
         }
         else if (args != null) {
            //  委托给构造函数 getBean() 处理
            return (T) parentBeanFactory.getBean(nameToLookup, args);
         }
         else {
            // 没有 args,委托给标准的 getBean() 处理
            return parentBeanFactory.getBean(nameToLookup, requiredType);
         }
      }

      /**
       * 方法参数 typeCheckOnly ,是用来判断调用 #getBean(...) 方法时,表示是否为仅仅进行类型检查获取 Bean 对象
       * 如果不是仅仅做类型检查,而是创建 Bean 对象,则需要调用 #markBeanAsCreated(String beanName) 方法,进行记录
       */
      if (!typeCheckOnly) {
         markBeanAsCreated(beanName);
      }

      try {
         final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         //检查当前创建的bean定义是不是抽象的bean定义
         checkMergedBeanDefinition(mbd, beanName, args);

         /**
            *
           * @Bean
            public DependsA dependsA() {
               return new DependsA();
            }

             @Bean
             @DependsOn(value = {"dependsA"})
             public DependsB dependsB() {
                return new DependsB();
             }
          * 处理dependsOn的依赖(这个不是我们所谓的循环依赖 而是bean创建前后的依赖)
          */
         //依赖bean的名称
         String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
            // <1> 若给定的依赖 bean 已经注册为依赖给定的 bean
            // 即循环依赖的情况,抛出 BeanCreationException 异常
            for (String dep : dependsOn) {
               //beanName是当前正在创建的bean,dep是正在创建的bean的依赖的bean的名称
               if (isDependent(beanName, dep)) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
               }
               //保存的是依赖 beanName 之间的映射关系:依赖 beanName - > beanName 的集合
               registerDependentBean(dep, beanName);
               try {
                  //获取depentceOn的bean
                  getBean(dep);
               }
               catch (NoSuchBeanDefinitionException ex) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
               }
            }
         }

         //创建单例bean
         if (mbd.isSingleton()) {
            //把beanName 和一个singletonFactory 并且传入一个回调对象用于回调
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  //进入创建bean的逻辑
                  return createBean(beanName, mbd, args);
               }
               catch (BeansException ex) {
                  //创建bean的过程中发生异常,需要销毁关于当前bean的所有信息
                  destroySingleton(beanName);
                  throw ex;
               }
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }

         else if (mbd.isPrototype()) {
            // 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 {
            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;
      }
   }

   // Check if required type matches the type of the actual bean instance.
   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;
}

getSingleton

image.png

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   /**
    * 第一步:我们尝试去一级缓存(单例缓存池中去获取对象,一般情况从该map中获取的对象是直接可以使用的)
    * IOC容器初始化加载单实例bean的时候第一次进来的时候 该map中一般返回空
    */
   Object singletonObject = this.singletonObjects.get(beanName);
   /**
    * 若在第一级缓存中没有获取到对象,并且singletonsCurrentlyInCreation这个list包含该beanName
    * IOC容器初始化加载单实例bean的时候第一次进来的时候 该list中一般返回空,但是循环依赖的时候可以满足该条件
    */
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
         /**
          * 尝试去二级缓存中获取对象(二级缓存中的对象是一个早期对象)
          * 何为早期对象:就是bean刚刚调用了构造方法,还来不及给bean的属性进行赋值的对象(纯净态)
          * 就是早期对象
          */
         singletonObject = this.earlySingletonObjects.get(beanName);
         /**
          * 二级缓存中也没有获取到对象,allowEarlyReference为true(参数是有上一个方法传递进来的true)
          */
         if (singletonObject == null && allowEarlyReference) {
            /**
             * 直接从三级缓存中获取 ObjectFactory对象 这个对接就是用来解决循环依赖的关键所在
             * 在ioc后期的过程中,当bean调用了构造方法的时候,把早期对象包裹成一个ObjectFactory
             * 暴露到三级缓存中
             */
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            //从三级缓存中获取到对象不为空
            if (singletonFactory != null) {
               /**
                * 在这里通过暴露的ObjectFactory 包装对象中,通过调用他的getObject()来获取我们的早期对象
                * 在这个环节中会调用到 getEarlyBeanReference()来进行后置处理
                */
               singletonObject = singletonFactory.getObject();
               //把早期对象放置在二级缓存,
               this.earlySingletonObjects.put(beanName, singletonObject);
               //ObjectFactory 包装对象从三级缓存中删除掉
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}

这里的逻辑与上面的Demo示例类似的逻辑,继续往下走的源码在前面的文章中已有,这里不做赘述。
问题:

  • 为什么使用二级缓存和三级缓存?上面的已讲清楚
  • spring有没有解决构造函数的循环依赖?没有,因为构造函数还在实例化的过程中,连实例都没有,无法解决循环依赖。会直接报错。
  • spring有没有解决多例下的循环依赖?多例创建是不存在一级缓存中,因此不能解决。