Spring 循环依赖解析

1,480 阅读4分钟

什么是循环依赖

说简单一点就是,A对象有一个属性是B,B对象有一个属性是A,当A对象生成的时候依赖B对象,B对象生成的时候需要依赖A对象,这个时候就很形成了一个闭环。

如何解决循环依赖问题

我的processOn地址:www.processon.com/view/60667f…

Spring内部通过三级缓存来解决循环依赖的问题。

核心类:DefaultSingletonBeanRegistry

所谓三级缓存,就是spring容器解决循环依赖的三个map:

一级缓存:singletonObjects,是一个ConcurrentHashMap,也叫单例池,存放已经经历了完整生命周期的Bean对象;

二级缓存:earlySingletonObjects,是一个HashMap,存放早期暴露出来的Bean对象,Bean的生命周期未结束,属性还未填充完整;

三级缓存:singletonFactories,第三级缓存value是一个工厂,三级缓存存放可以生成bean的工厂。

图片解析

TIM截图20220323204241.png

源码解析

循环依赖中最重要的就是getSingleton方法

这个getSingleton方法是在doGetBean方法刚进去的时候用于查看三个缓存中是否有数据,如果有则从三个缓存中获取对应数据

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) {
            //二级缓存取不到取三级缓存 三级缓存实在实列化之后加入
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               singletonObject = singletonFactory.getObject();
               //移除三级缓存加入二级缓存
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}

这个getSingleton方法是在doGetBean方法后面,也就是createbean的入口方法,走到这里说明一级二级三级缓存中都没有bean信息要创建bean信息,创建完后放入一级缓存。

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 {
             //调用createBean方法
            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) {
            //bean完整创建后加入一级缓存删除二级三级缓存
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}

再提一嘴,在doCreatebean中有这样一段代码

image.png 这里也调用了getSingleton方法,但是这里主要是针对于循环依赖的代理所使用 如A依赖B,B依赖A,创建A注入B,创建B注入A,在三级缓存获取A后对A进行了代理,B注入了A的代理对象,A的代理放入二级缓存,进入然后B创建完成,于时返回到A继续初始化,这里就会有一个问题,A还是原来的A,但是B却是注入了代理的A,于时就需要再去调用getSinleton方法将二级缓存中的代理对象取出并且替换掉A,然后在生成A的代理放入一级缓存。

三级缓存的思考

为什么要使用三级缓存?两个字:代理

两级缓存解决代理问题:1.使用双Map<String,Object> 理论上可以解决依赖注入,同时也可以解决代理问题,但是这就意味着每次加入二级缓存都要是代理的对象,如果没有依赖注入完全没有必要,同时也不符合Spring对bean生命周期的定义(对象都应该在创建之后在进行动态代理而不是单纯的实例化以后就急着进行代理,如果是依赖注入那是不得已)

2.使用单Map<String,Object>和单Map<String,ObjectFactory> 可以解决依赖注入问题但是却无法解决代理问题,原因:若要进行代理那就是从ObjectFactory中获取对象实例的时候进行代理,但是这样子每次获取的对象都不是同一个,若是三级缓存代理可以放入二级缓存,其他对象拿取都可以获得同一个代理对象。

其他思考: 解决循环依赖基本上都是利用多级缓存,mybatis中解决循环依赖也是如此,这是一个非常值得借鉴的地方。

这都是本人对于Spring循环依赖的理解,若有问题请大佬指出,非常感谢!