• Spring大白话--三级缓存解决循环依赖问题



    前言

    在使用Spring 开发过程中,我们需要对定义后的bean 通过构造方法,或者bean 注入的方式注入到某个类中进而使用改bean 对应的方法,在此过程中就会出现一个类中注入了n多个bean,几个类中bean 相互注入,出现 A 依赖B,B依赖C,C又依赖A/B 这种循环情况的出现。


    提示:以下是本篇文章正文内容,下面案例可供参考

    一、Spring 循环依赖是什么?

    循环依赖是指在Spring容器中,存在两个或多个Bean之间的互相依赖关系,形成了一个闭环的依赖链。具体来说,当Bean A依赖Bean B,同时Bean B又依赖Bean A时,就产生了循环依赖。
    循环依赖可能会导致以下问题:

    • 死锁:如果循环依赖的解析过程不正确,可能会导致死锁。当容器无法确定如何先实例化哪个Bean时,可能会造成死锁情况,导致应用程序无法继续正常执行。
      在这里插入图片描述

    • 未完全初始化的Bean:在解决循环依赖时,Spring使用了代理对象来解决依赖问题。这意味着当Bean A注入到Bean B中时,A可能是一个未完全初始化的代理对象,而不是完全实例化的对象。这可能导致在早期阶段的Bean存在一些限制和潜在的问题。

    为了解决循环依赖的问题,Spring使用了一个两阶段的解析过程:实例化阶段和注入阶段。在实例化阶段,Spring创建对象并将其放入缓存中;在注入阶段,Spring解决依赖关系并完成注入。

    SpringBoot 从 2.6 之前默认开启循环依赖,之后 开始默认不允许出现 Bean 循环引用,如果需要则进行手动开启:

    spring:
      main:
        allow-circular-references:true
    
    • 1
    • 2
    • 3

    二、Spring 三级缓存解决单例的循环依赖:

    Spring 三级缓存 实际上使用了3个Map 来打破单例对象循环依赖的问题,singletonObjects:这是一级缓存,保存已经实例化且完成了所有的依赖注入和初始化的单例bean实例。earlySingletonObjects:这是二级缓存,保存已经实例化但尚未完成所有的依赖注入和初始化的单例bean实例。它主要用于解决属性注入时的循环依赖问题。singletonFactories:这是三级缓存,保存创建单例bean实例的ObjectFactory。当创建bean时,Spring首先会尝试从singletonFactories中获取bean实例的ObjectFactory,用于创建bean实例。一旦bean创建完成,会将其从singletonFactories中移除。

    2.1 Bean 单例对象生成的过程:

    对象的生成必须先通过其构造方法进行实例化,然后对其属性赋值完成初始化;以下以 Aservice ,Bservice,Cservice 为例进行研究;

    public interface Aservice {
    }
    public interface Bservice {
    }
    public interface Cservice {
    }
    @Service
    public class AserviceImpl implements Aservice {
        @Autowired
        private Bservice bservice;
    
    }
    @Service
    public class BserviceImpl implements Bservice {
    
        @Autowired
        private Aservice aservice;
    
    }
    @Service
    public class CserviceImpl implements Cservice {
        @Autowired
        private Aservice aservice;
        @Autowired
        private Bservice bservice;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    其中Aservice 依赖Bservice 的bean ,Bservice 依赖Aservice 的bean,Cservice 依赖Aservice ,Bservice 的bean ;

    在这里插入图片描述

    • 首先 A 实例化,通过A 类的构造方法进行 实例的构建并返回; 对bservice 进行 属性值进行设置;
    • 此时需要先从单例池中去获取,Bservice 的bean ,Bservice 的bean 还没有被创建,所以此时需要先创建Bservice 的bean ;
    • 调用Bservice 的构造方法进行实例的创建返回,然后 对Aservice 进行 属性值进行设置;

    然后在从单例池中获取Aservice 的bean,发现没有Aservice 的bean ,这个时候如果重复在走Aservice 的bean 创建过程就会陷入死循环,显然我们的项目此时是可以成功启动的,也即没有陷入死循环中,那么Spring 是怎么解决的?

    如果说在创建Aservice 的bean时是分为两步:

    • 步骤1: 先通过其构造方法完成实例化;
    • 步骤2: 对其属性进行填充;

    那么如果我们在步骤1 之后 ,就将还没有完成初始化的Aservice 的bean 放入到某个地方,然后在初始化其他bean 的时候 如果发现依赖了Aservice 的bean 此时可以直接注入Aservice 的bean完成对其的引用,即使Aservice 的bean还没有进行完整的初始化,我们进行了提前暴露,这样在Aservice 的bean真正完成初始化之后,对Aservice 的bean引用也随即完成;这样就打破了bean 的循环依赖,bean 可以正常初始化了;
    现在Bservice,Cservice 中都依赖了Aservice 的bean ,显然无法对Aservice 的单例bean 实例化两次 ,那么就需要有个地方来存放Aservice 的这个还没有完全初始化的bean,这样后续其它的bean 在注入Aservice 的bean 时 会发现 Aservice 的bean 已经有了,所以就可以直接使用,不需要在额外创建,这里spring 使用 map (二级缓存)来存放已经实例化,但是还没有完全初始化的 bean , 以便于在发生循环依赖时,如果从单例池中获取不到对应的bean 就到二级缓存中在获取一次,如果获取到了可以直接使用,如果获取不到则需要去生成这个bean 并将其放入到二级缓存中;
    在这里插入图片描述
    因为Bservice 中已经将Aservice的bean 放入到了二级缓存中,所以Cservice 可以直接从二级缓存中获取到service 的单例bean ;
    到此看起来spring 已经通过二级缓存来提前暴露未初始化完成的bean 而解决了循环依赖,那么为什么还有三级缓存的概念?
    在原码中可以看到三级缓存也是一个map ,并且其value 存的是一个对象的工厂

    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
    
    • 1

    我们可能已经听说过三级缓存放入的是Lambda表达式 的匿名函数,这个函数会在使用到的时候被调用,那么spring 为什么选择放一个匿名函数而不是直接放入一个bean 呢;显然如果直接放入一个bean 那么三级缓存的作用就和二级缓存相同了;所以spring 这样做肯定是有一些原因的,先来看下在原码中三级缓存放入的Lambda是什么:

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        SmartInstantiationAwareBeanPostProcessor bp;
        if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
            for(Iterator var5 = this.getBeanPostProcessorCache().smartInstantiationAware.iterator(); var5.hasNext(); exposedObject = bp.getEarlyBeanReference(exposedObject, beanName)) {
                bp = (SmartInstantiationAwareBeanPostProcessor)var5.next();
            }
        }
    
        return exposedObject;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    从以上代码可以执行先把初始的 bean 对象赋给了 exposedObject ,然后如果发现这个bean 是否满足mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()条件,进而判断当前的bean是否是一个合成的代理对象。在AOP中,合成代理对象是通过特定的机制(如JDK动态代理或CGLIB动态代理)创建的。这个条件可以用来检查当前创建的bean是否是这种合成的代理对象

    如果需要合成代理对象则 进入exposedObject = bp.getEarlyBeanReference(exposedObject, beanName) 进行代理对象的创建:
    AbstractAutoProxyCreator.getEarlyBeanReference

       // 省略代码
    public Object getEarlyBeanReference(Object bean, String beanName) {
      Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
         this.earlyProxyReferences.put(cacheKey, bean);
         return this.wrapIfNecessary(bean, beanName, cacheKey);
     }
    Object exposedObject = bean;
    
     try {
     	// 递归填充改bean 的其他属性
         this.populateBean(beanName, mbd, instanceWrapper);
         exposedObject = this.initializeBean(beanName, exposedObject, mbd);
     } catch (Throwable var18) {
         if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
             throw (BeanCreationException)var18;
         }
    
         throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
     }
       // 省略代码
     protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
             return bean;
         } else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
             return bean;
         } else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
             Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
             if (specificInterceptors != DO_NOT_PROXY) {
                 this.advisedBeans.put(cacheKey, Boolean.TRUE);
                 Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                 this.proxyTypes.put(cacheKey, proxy.getClass());
                 return proxy;
             } else {
                 this.advisedBeans.put(cacheKey, Boolean.FALSE);
                 return bean;
             }
         } else {
             this.advisedBeans.put(cacheKey, Boolean.FALSE);
             return bean;
         }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    从上面代码可以看到 如果这个对象在缓存中没有而且是不能被跳过的 则使用this.createProxy 为其生产代理对象并进行返回;所以使用了这个Lambda表达式目的就是返回一个普通对象的bean 或者代理对象的bean,那么为什么不直接在bean 实例化之后,就直接调用getEarlyBeanReference 方法,这样将生成的普通对象或者代理对象的bean 直接放入到二级缓存中,这样岂不是更为直接,显然这样做也是可以解决spring 的循环依赖的问题,而且在二级缓存中存放的对象就是普通对象或者代理生成的对象。
    虽然可以这样做但是违反了spring 对代理对象生成的原则,Spring 的设计原则是尽可能保证普通对象创建完成之后,再生成其 AOP 代理(尽可能延迟代理对象的生成),因为这样做的话,所有代理都提前到了实例化之后,初始化阶段前,显然与尽可能延迟代理对象的生成 原则是违背的。所以在此使用 Lambda表达式 ,在真正需要创建对象bean 的提前引用时,才通过 Lambda表达式 来进行创建 ,来遵循尽可能延迟代理对象的生成 原则

    没有依赖,有AOP 这种情况中,我们知道 AOP 代理对象的生成是在成品对象创建完成之后创建的,这也是 Spring 的设计原则,代理对象尽量推迟创建,循环依赖 + AOP 这种情况中, 代理对象的生成提前了,因为必须要保证其 AOP 功能,那么在bean 初始化完成之后,又到了要对改对象进行代理增强的环节,此时spring 又是怎么判断改bean 已经被增强为代理对象,而不需要重新创建代理对象?

    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
         if (bean != null) {
             Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
             if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                 return this.wrapIfNecessary(bean, beanName, cacheKey);
             }
         }
    
         return bean;
     }
     public Object getEarlyBeanReference(Object bean, String beanName) {
            Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
            this.earlyProxyReferences.put(cacheKey, bean);
            return this.wrapIfNecessary(bean, beanName, cacheKey);
        }
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    从以上源码中可以看到 在postProcessAfterInitialization bean 被初始化完成之后执行的方法 从this.getCacheKey 获取到的cacheKey ,最后比较两个bean 是否是一个,如果是则说明bean 已经进行过代理,否则则重新执行wrapIfNecessary 生成代理对象

    2.2 三级缓存工作过程:

    既然在spring 容器中bean 是单例的,那么就不可能存在改bean 的多个对象,也即对bean 的所有引用都指向同一个对象;此时就有一个问题,当一个bean 被依赖注入时,怎么知道这个单例的bean 是否已经被初始化?
    所以就需要将已经完成初始化的bean 放入到一个地方中,这个地方要满足如果这个bean 已经被初始化过了,则不需要进行在进行初始化,而且存和取都比较方便,spring 选用map 来对其进行存储,其中key 为bean的那么,value 为单列bean:

      private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    
    • 1

    当Spring创建一个单例bean时,会先检查一级缓存(singletonObjects),如果在其中找到bean实例,则直接返回。如果没有找到,则继续执行bean的创建流程,并将工厂方法存储在三级缓存(singletonFactories)中
    在创建bean的过程中,如果存在循环依赖问题,Spring会先尝试从二级缓存(earlySingletonObjects)中获取bean实例的早期引用,以解决循环依赖。如果早期引用不存在,就会使用三级缓存(singletonFactories)中的工厂方法创建bean实例
    一旦bean创建完成,会将其放入一级缓存(singletonObjects),并从二级缓存(earlySingletonObjects)和三级缓存(singletonFactories)中移除
    AbstractBeanFactory,doGetBean 获取对应的bean,此处只列举关键代码

    protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
    /**
    ** 省略代码
    **/ 
      // 会先检查一级缓存(singletonObjects),如果在其中找到bean实例,则直接返回,此方法调用DefaultSingletonBeanRegistry 下getSingleton 方法
      Object sharedInstance = this.getSingleton(beanName);
      /**
    ** 省略代码
    **/ 
    // 如果没有找到,则继续执行bean的创建流程,并将工厂方法存储在三级缓存(singletonFactories)中
     if (mbd.isSingleton()) {
     	// this.getSingleton 通过改bean 的工厂方法创建出来bean 并放入到单例池中
       sharedInstance = this.getSingleton(beanName, () -> {
            try {
            	// 创建普通对象/代理对象 并将工厂方法存储在三级缓存(singletonFactories)中 
            	// 此方法调用  AbstractAutowireCapableBeanFactory 下 createBean 方法然后在调用 doCreateBean 方法
                return this.createBean(beanName, mbd, args);
            } catch (BeansException var5) {
                this.destroySingleton(beanName);
                throw var5;
            }
        });
        beanInstance = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    Object sharedInstance = this.getSingleton(beanName) 方法,DefaultSingletonBeanRegistry 下getSingleton, 会先检查一级缓存(singletonObjects),如果在其中找到bean实例,则直接返回:

    @Nullable
    public Object getSingleton(String beanName) {
         return this.getSingleton(beanName, true);
     }
     @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从一级缓存获取完整的bean
      Object singletonObject = this.singletonObjects.get(beanName);
      // 没有获取到 并且 这个bean 正在初始化中
      if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
      	// 从二级缓存获取这个bean 的早期引用
          singletonObject = this.earlySingletonObjects.get(beanName);
          // 没有早期引用 并且运行循环依赖
          if (singletonObject == null && allowEarlyReference) {
              synchronized(this.singletonObjects) {
              // 加对象锁
                  singletonObject = this.singletonObjects.get(beanName);
                  if (singletonObject == null) {
                      singletonObject = this.earlySingletonObjects.get(beanName);
                      if (singletonObject == null) {
                      	// 从三级缓存 获取bena 的 工厂类
                          ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                          if (singletonFactory != null) {
                              singletonObject = singletonFactory.getObject();
                              //  二级缓存中放入
                              this.earlySingletonObjects.put(beanName, singletonObject);
                              // 三级缓存中移除
                              this.singletonFactories.remove(beanName);
                          }
                      }
                  }
              }
          }
      }
    
      return singletonObject;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    单例池中没有改bean 则进入 sharedInstance = this.getSingleton(beanName, () -> {}) return this.createBean(beanName, mbd, args) 方法通过AbstractAutowireCapableBeanFactory.doCreateBean 创建普通对象/代理对象 并将工厂方法存储在三级缓存(singletonFactories)中:

    boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
    if (earlySingletonExposure) {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
        }
    	//  addSingletonFactory 放入到三级缓存
        this.addSingletonFactory(beanName, () -> {
        	// 获取提前暴露出来的bean 对象
            return this.getEarlyBeanReference(beanName, mbd, bean);
        });
    }
    try {
    	// 继续对改bean 中的属性进行初始化
        this.populateBean(beanName, mbd, instanceWrapper);
         exposedObject = this.initializeBean(beanName, exposedObject, mbd);
     } catch (Throwable var18) {
         if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
             throw (BeanCreationException)var18;
         }
    
         throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
     }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    DefaultSingletonBeanRegistry.addSingletonFactory: bean的工厂放入到三级缓存:

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
           Assert.notNull(singletonFactory, "Singleton factory must not be null");
           synchronized(this.singletonObjects) {
               if (!this.singletonObjects.containsKey(beanName)) {
               		// 放入3级缓存map
                   this.singletonFactories.put(beanName, singletonFactory);
                   this.earlySingletonObjects.remove(beanName);
                   // 将改bean 的名称放入到正在创建的bean 的set 集合
                   this.registeredSingletons.add(beanName);
               }
    
           }
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在对改bean 的工厂放入到三级缓存之后,继续调用 sharedInstance = this.getSingleton(beanName, () -> {}) , this.getSingleton 方法:

    DefaultSingletonBeanRegistry.getSingleton 将对应的bean 通过 Lambda表达式 生成对应的bean ,然后最终将改bean 放入到单例池中;

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
         Assert.notNull(beanName, "Bean name must not be null");
         synchronized(this.singletonObjects) {
         	//  获取对象锁,然后从单例池中获取bean
             Object singletonObject = this.singletonObjects.get(beanName);
             if (singletonObject == null) {
                // 省略代码
    
                 try {
                 	// 获取bean 的工厂
                     singletonObject = singletonFactory.getObject();
                     newSingleton = true;
                 } 
      			// 省略代码
                 if (newSingleton) {
                 // 放入到单例翅中
                     this.addSingleton(beanName, singletonObject);
                 }
             }
    
             return singletonObject;
         }
     }
    protected void addSingleton(String beanName, Object singletonObject) {
         synchronized(this.singletonObjects) {
         	// 单例池中放入改bean
             this.singletonObjects.put(beanName, singletonObject);
             // 从三级缓存和二级缓存中移除
             this.singletonFactories.remove(beanName);
             this.earlySingletonObjects.remove(beanName);
             this.registeredSingletons.add(beanName);
         }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在A 依赖于B,B 依赖于C,C又依赖A 的场景中,当初始化 A 时,会先实例化 B,然后再实例化 C,然后再实例化 A:

    • 开始初始化 A,创建 A 的实例并完成属性注入(如果有);
    • 当初始化 A 过程中发现 A 依赖于 B,Spring 会尝试创建 B 的实例并完成属性注入;
    • 创建 B 的实例时发现 B 还有其他依赖,其中包 C。此时 Spring 会首先尝试创建 C 的实例并完成属性注;
    • 在初始化 C 过程中,发现 C 依赖于 A,此时 A 实例还未创建完成,因此会暂时将 A 的实例设置为一个 Early Reference(早期引用),并将 C 的依赖关系注册到 A 的 Dependent 对象中;
    • 继续初始化 C,并完成属性注入(如果有);
    • 完成 C 的实例化后,继续初始化 B,并完成属性注入(如果有)
    • 在初始化 B 过程中,发现 B 不再依赖其他对象,完成 B 的初始化;
    • 继续初始化 A,在初始化 A 过程中,发现 A 依赖于 B,B 的实例早已创建完成,因此可以直接获取 B 的实例;
    • 完成 A 的实例化,并完成属性注入(如果有)

    Spring 使用了缓存机制,确保在递归调用时能够正确地获取到已经创建的对象实例,避免死循环。同时,Spring 也会处理代理对象的生成和使用,以确保 A、B、C 的代理对象在正确的时间被创建和使用。

    三、Spring 三级缓存无法解决的单例循环依赖情况:

    3.1 通过构造方法注入的bean ,出现循环依赖会报错:

    在A类中通过构造方法的方式注入B的bean,在B类中通过构造方法的方式注入A的bean;在此场景中,

    • 在调用A的构造方法进行实例化时,发现依赖的B的bean,需要对B类进行实例化;
    • 调用B类的构造方法进行实例化时,发现依赖的A的bean,此时出现循环依赖;
    • 然后此时需要对A的bean 提前进行引用的暴露;
    • 然而在对A的bean 提前进行引用的暴露,需要用到A 的实例化对象,此时A的实例化对象还没有被创建,则直接报错;

    3.2 早期暴露的非aop代理对象引用,出现循环依赖会报错:

    @Service
    public class AserviceImpl implements Aservice {
        @Autowired
        private Bservice bservice;
    
        @Async
        public  void test(){
            System.out.println(bservice);
        }
    
    }
    @Service
    public class BserviceImpl implements Bservice {
    
        @Autowired
        private Aservice aservice;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    当使用 @Async 注解标注一个bean 中的方法为异步方法时,Bservice 中注入的Aservice aservice 的bean 与最终生成的Aservice 的bean 不相同而导致报错

    • 对Aservice 进行实例化后,对其bservice 属性进行初始化;
    • 对Bservice 进行实例化,然后对其aservice 属性进行初始化,此时发现循环依赖;
    • 暴露Aservice 的bean 到二级缓存中,因为Aservice 非aop 代理对象 ,所以此时二级缓存中放入的是Aservice 的普通对象;
    • Bservice 的bean 完成初始化;
    • Aservice 对 bservice 属性初始化完成 ,并将Aservice 的bean 放入到一级缓存,并从二级缓存中删除;
    • 对Aservice 的bean 进行代理对象的包装,包装后的bean 与之前放入到一级缓存的bean 两个不是同一个,程序报错;

    四、Lazy 注解解决循环依赖问题:

    延迟加载可以通过将 bean 的依赖关系运行时进行注入,而不是在初始化阶段。这样,当遇到循环依赖时,Spring 可以先创建需要的 bean 实例,并将其设置为代理对象,而不需要立即解决依赖关系。

    @Service
    public class AserviceImpl implements Aservice {
        @Autowired
        @Lazy
        private Bservice bservice;
    
        @Async
        public  void test(){
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Lazy 延迟加载打破循环依赖; 通过其它途径生成bservice 的lazy 的代理对象,不会去走创建bservice 的代理 对象 然后注入aservice 这套流程。这样创建aservice 的单例对象并放入到单例池中,Bservice 的bean 在实例化后,注入aservice bean 属性就可以从单例池中加载到aservice 的真正的bean ,而不会出现bean 对象不一致的问题。

    总结:

    spring 通过三级缓存解决单例的循环依赖问题,singletonObjects 用来存放已经初始化完成的bean,earlySingletonObjects 用来存放早期暴露出来的半成品bean 的引用,singletonFactories 用来存放获取早期引用的 Lambda表达式 工厂。

    参考:

    Spring的三级缓存整理;
    从源码角度,带你研究什么是三级缓存;

  • 相关阅读:
    SwiftUI 通过 ARKit 使用微笑控制 SwiftUI 视图(教程含源码)
    论文阅读之Improved Word Representation Learning with Sememes(2017)
    【云原生Kubernetes系列第三篇】二进制部署单节点Kubernetes(k8s)v1.20
    Chapter5.6:频率响应法考研参考题
    迷茫内耗的一天
    mysql巧妙化解递归查询树形数据 | 纯sql
    FME针对尖锐角及小缝隙处理
    HarmonyOS/OpenHarmony应用开发-DevEco Studio新建项目的整体说明
    Github的2FA验证问题的丝滑解决方案 ||(Verify your two-factor authentication (2FA) settings)
    基于 SSM 框架的旅游文化管理平台
  • 原文地址:https://blog.csdn.net/l123lgx/article/details/133234121