spring相互依赖怎么解决_Spring 怎么解决循环依赖

本文详细介绍了Spring如何处理循环依赖问题,特别是对于单例bean的处理。通过分析Spring源码,展示了从`refresh()`方法到`preInstantiateSingletons()`方法,以及在`doCreateBean()`中的关键步骤,包括一级、二级和三级缓存的角色。在循环依赖的场景下,Spring利用这些缓存来避免无限递归,确保bean的正确初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2020年8月11日 浙江大华面试原题

   你看过spring源码吗,spring是怎么解决循环依赖的?

  1. 出现spring依赖的场景

import org.springframework.stereotype.Service;/** * Created by wt on 2020/9/12. * * @author wt */@Servicepublic class BeanA {    private  BeanB b;    @Autowired   //注意是构造器注入    public BeanA(BeanB b){        this.b=b;    }}
package com.wtxm.springboot.beans;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;/** * Created by wt on 2020/9/12. * * @author wt */@Servicepublic class BeanB {    private BeanA a;    @Autowired    public BeanB(BeanA a) {        this.a = a;    }}

  如下图,BeanA和BeanB 互相依赖

303f95957b1af91abf1e8f8134e2be78.png启动项目

***************************APPLICATION FAILED TO START***************************Description:The dependencies of some of the beans in the application context form a cycle:┌─────┐|  beanA defined in file [D:\code\2020\springboot\target\classes\com\wtxm\springboot\beans\BeanA.class]↑     ↓|  beanB defined in file [D:\code\2020\springboot\target\classes\com\wtxm\springboot\beans\BeanB.class]└─────┘Disconnected from the target VM, address: '127.0.0.1:61392', transport: 'socket'Process finished with exit code 1

这就是所谓的循环依赖问题

2.怎么解决循环依赖  

使用单例bean,使用属性注入的方式,不采用构造器注入方式

3.Spring 解决循环依赖的原理

1.基础知识

对象实例化过程=当前对象实例化和对象属性的实例化

2.Spring 创建Bean的步骤

①createBeanInstance:初始化对象,类似于clazz.newInstance()

②populateBean:填充属性,bean的对象依赖属性就是在这一步进行填充

③initializeBean:调用spring配置中的init-method方法

经过上述三步,一个spring的bean才被完全创建。

4.源码分析:跟着方法调用链一步一步看源码

AbstractApplicationContext 的refersh()方法 是spring容器启动的重要方法,也是我们分析bean注入的入口

public void refresh() throws BeansException, IllegalStateException {        synchronized(this.startupShutdownMonitor) {            this.prepareRefresh();            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();            this.prepareBeanFactory(beanFactory);            try {                this.postProcessBeanFactory(beanFactory);                this.invokeBeanFactoryPostProcessors(beanFactory);                this.registerBeanPostProcessors(beanFactory);                this.initMessageSource();                this.initApplicationEventMulticaster();                this.onRefresh();                this.registerListeners();                this.finishBeanFactoryInitialization(beanFactory);                this.finishRefresh();            } catch (BeansException var9) {                if (this.logger.isWarnEnabled()) {                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);                }                this.destroyBeans();                this.cancelRefresh(var9);                throw var9;            } finally {                this.resetCommonCaches();            }        }    }

我们关注  finishBeanFactoryInitialization(beanFactory) 这个方法

此方法的作用是完成bean工厂的实例化工作

这个方法最后一行是核心代码beanFactory.preInstantiateSingletons();  

进入DefaultListableBeanFactory这个类的preInstantiateSingletons()方法 。顾名思义,这个方法是初始化单例bean ,打断点调试,发现了我们的目标bean对象

cd2d977fe74748bd69c645f08bf8a030.png

找到填充bean 的核心方法

88d5b9dfc1b51503ba4bee726337134d.png

protected  T doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {        String beanName = this.transformedBeanName(name);        //非常重要的方法 会详细解释        Object sharedInstance = this.getSingleton(beanName);        Object bean;        //第一次创建beanA 跳过此个if 因为sharedIntance是Null        if (sharedInstance != null && args == null) {            if (this.logger.isTraceEnabled()) {                if (this.isSingletonCurrentlyInCreation(beanName)) {                    this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");                } else {                    this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");                }            }            bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition) null);        } else {            if (this.isPrototypeCurrentlyInCreation(beanName)) {                throw new BeanCurrentlyInCreationException(beanName);            }            BeanFactory parentBeanFactory = this.getParentBeanFactory();            //第一次创建beanA对象 会跳过此次if parentBeanFactory为null            if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {                String nameToLookup = this.originalBeanName(name);                if (parentBeanFactory instanceof AbstractBeanFactory) {                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);                }                if (args != null) {                    return parentBeanFactory.getBean(nameToLookup, args);                }                if (requiredType != null) {                    return parentBeanFactory.getBean(nameToLookup, requiredType);                }                return parentBeanFactory.getBean(nameToLookup);            }            if (!typeCheckOnly) {                this.markBeanAsCreated(beanName);            }            try {                RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);                this.checkMergedBeanDefinition(mbd, beanName, args);                String[] dependsOn = mbd.getDependsOn();                String[] var11;                //第一次创建beanA对象 dependsOn为null                if (dependsOn != null) {                    var11 = dependsOn;                    int var12 = dependsOn.length;                    for (int var13 = 0; var13 < var12; ++var13) {                        String dep = var11[var13];                        if (this.isDependent(beanName, dep)) {                            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");                        }                        this.registerDependentBean(dep, beanName);                        try {                            this.getBean(dep);                        } catch (NoSuchBeanDefinitionException var24) {                            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", var24);                        }                    }                }                if (mbd.isSingleton()) {                    //第一次创建beanA对象来到这                    //这是 第二个需要重点关注的地方 会重点解释                    sharedInstance = this.getSingleton(beanName, () -> {                        try {                            return this.createBean(beanName, mbd, args);                        } catch (BeansException var5) {                            this.destroySingleton(beanName);                            throw var5;                        }                    });                    bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);                } else if (mbd.isPrototype()) {                    var11 = null;                    Object prototypeInstance;                    try {                        this.beforePrototypeCreation(beanName);                        prototypeInstance = this.createBean(beanName, mbd, args);                    } finally {                        this.afterPrototypeCreation(beanName);                    }                    bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);                } else {                    String scopeName = mbd.getScope();                    if (!StringUtils.hasLength(scopeName)) {                        throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");                    }                    Scope 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, () -> {                            this.beforePrototypeCreation(beanName);                            Object var4;                            try {                                var4 = this.createBean(beanName, mbd, args);                            } finally {                                this.afterPrototypeCreation(beanName);                            }                            return var4;                        });                        bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);                    } catch (IllegalStateException var23) {                        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", var23);                    }                }            } catch (BeansException var26) {                this.cleanupAfterBeanCreationFailure(beanName);                throw var26;            }        }        if (requiredType != null && !requiredType.isInstance(bean)) {            try {                T convertedBean = this.getTypeConverter().convertIfNecessary(bean, requiredType);                if (convertedBean == null) {                    throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());                } else {                    return convertedBean;                }            } catch (TypeMismatchException var25) {                if (this.logger.isTraceEnabled()) {                    this.logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", var25);                }                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());            }        } else {            return bean;        }    }

这个方法非常长,我们只关注几个非常重要的地方

7938cf0714eedf1b5b21493021872418.png

第一个关注的地方是getSingleton()

  @Nullable    protected Object getSingleton(String beanName, boolean allowEarlyReference) {        Object singletonObject = this.singletonObjects.get(beanName);        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {            synchronized (this.singletonObjects) {                singletonObject = this.earlySingletonObjects.get(beanName);                if (singletonObject == null && allowEarlyReference) {                    ObjectFactory> singletonFactory = (ObjectFactory) this.singletonFactories.get(beanName);                    if (singletonFactory != null) {                        singletonObject = singletonFactory.getObject();                        this.earlySingletonObjects.put(beanName, singletonObject);                        this.singletonFactories.remove(beanName);                    }                }            }        }        return singletonObject;    }

singletonObjects 是单例对象集合,我们称之为一级缓存。因为我们是第一次创建BeanA对象,并且此对象是最初创建,故返回null (截图也可以看出来)  。isSingletonCurrentlyInCreation 这个属性表示当前Bean对象是否是正在创建,只有往singletonsCurrentlyInCreation这个集合里添加了Bean,才能表示当前Bean正在创建。我们第一次创建BeanA对象并没有设置,故这个循环体我们进不去,但是第二次创建BeanA对象的时候,我们会进入这个循环体,下面会具体分析。

代码中标注 的第二个需要关注的地方   getSingleton()

 public Object getSingleton(String beanName, ObjectFactory> singletonFactory) {        Assert.notNull(beanName, "Bean name must not be null");        synchronized(this.singletonObjects) {        //从一级缓存中取beanA对象,此时肯定取不到            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 (this.logger.isDebugEnabled()) {                    this.logger.debug("Creating shared instance of singleton bean '" + beanName + "'");                }                this.beforeSingletonCreation(beanName);                boolean newSingleton = false;                boolean recordSuppressedExceptions = this.suppressedExceptions == null;                if (recordSuppressedExceptions) {                    this.suppressedExceptions = new LinkedHashSet();                }                try {                    singletonObject = singletonFactory.getObject();                    newSingleton = true;                } catch (IllegalStateException var16) {                    singletonObject = this.singletonObjects.get(beanName);                    if (singletonObject == null) {                        throw var16;                    }                } catch (BeanCreationException var17) {                    BeanCreationException ex = var17;                    if (recordSuppressedExceptions) {                        Iterator var8 = this.suppressedExceptions.iterator();                        while(var8.hasNext()) {                            Exception suppressedException = (Exception)var8.next();                            ex.addRelatedCause(suppressedException);                        }                    }                    throw ex;                } finally {                    if (recordSuppressedExceptions) {                        this.suppressedExceptions = null;                    }                    this.afterSingletonCreation(beanName);                }                if (newSingleton) {                    this.addSingleton(beanName, singletonObject);                }            }            return singletonObject;        }    } 

846c9b18d96c6cd501e456ee352a005c.png

singletonObjects这个集合 是存放单例对象的,显然此时一级缓存singletonObject还是为null,再看到beforeSingletonCreation()这个方法

  protected void beforeSingletonCreation(String beanName) {        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {            throw new BeanCurrentlyInCreationException(beanName);        }    }

打断点发现  inCreationCheckExclusions    为空,且做了一个非常重要的动作,就是往singletonsCurrentlyInCreation这个集合加了BeanA对象,这就是我们第二次创建BeanA对象进入不同的循环体的重要标识。退出getSingleton()方法,我们发现,传入该方法的参数是一个匿名对象,且createBean()实现了其接口的唯一方法T getObject() throws BeansException;

   if(mbd.isSingleton()){        sharedInstance = this.getSingleton(beanName, () -> {            try {                return this.createBean(beanName, mbd, args);            } catch (BeansException var5) {                this.destroySingleton(beanName);                throw var5;            }        });        bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);    }

这里creatBean是真正创建Bean的地方 核心方法是 doCreateBean

30cc70b2f7ae7b7e9f12983309d498e8.png

此时循环判断条件是当前Bean对象是单例,允许循环引用,且有正在被创建的属性,前面已经设置过,故earlySingletonExposure为true

 protected void addSingletonFactory(String beanName, ObjectFactory> singletonFactory) {        Assert.notNull(singletonFactory, "Singleton factory must not be null");        synchronized(this.singletonObjects) {            if (!this.singletonObjects.containsKey(beanName)) {                this.singletonFactories.put(beanName, singletonFactory);                this.earlySingletonObjects.remove(beanName);                this.registeredSingletons.add(beanName);            }        }    }

singletonFatories是三级缓存,添加了创建BeanA对象的BeanFactory;

earlySingletonObjects是二级缓存,清除了二级缓存中BeanA对象;

至此beanA对象初始化完毕,接着执行populateBean这个操作,发现BeanA有一个属性对象是BeanB类型。于是我们去初始化BeanB 类型的对象b,BeanB初始化的流程和beanA一模一样。

当BeanB 类型的Bean初始化完成之后,发现其有一个属性对象类型是BeanA,

接着spring就会继续去创建BeanA对象。但是这一次创建就和第一次创建beanA对象不一样了,我们接着来看那个超长的创建bean的方法

前面提到第一个需要关注的点

  protected Object getSingleton(String beanName, boolean allowEarlyReference) {        //从一级缓存中取到 此时singletonObject非null        Object singletonObject = this.singletonObjects.get(beanName);        //此前beanA对象已经被设置为 CurrentlyInCreation 故进入到循环体        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {            synchronized(this.singletonObjects) {              //从二级缓存中取,此前分析,二级缓存已经被清除,故进入循环体                singletonObject = this.earlySingletonObjects.get(beanName);                if (singletonObject == null && allowEarlyReference) {                 //获取创建beanA对象的工厂                    ObjectFactory> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);                    if (singletonFactory != null) {                    //获取工厂类                        singletonObject = singletonFactory.getObject();                        //存放二级缓存                         this.earlySingletonObjects.put(beanName, singletonObject);                        //清除三级缓存                        this.singletonFactories.remove(beanName);                    }                }            }        }      // 返回的是二级缓存中的beanA 此时BeanB 对象初始化完毕        return singletonObject;    } 

附加解释:

 AbstractAutowireCapableBeanFactory  类的 reateBeanInstance方法

ec34d31690d99ad3fe3fe730e8d470ee.png

这里可以开始解释为什么构造器注入不能解决循环依赖,可以自己点进去分析。

总结:

Spring第一次创建BeanA对象,在三级缓存中添加了创建BeanA对象的工厂,并删除了二级缓存中提前曝光的BeanA对象,初始化完毕之后,发现BeanA对象有一个BeanB类型的属性,接着去创建BeanB对象。BeanB对象的第一次创建过程和第一次创建BeanA对象一样。当初始化完BeanB对象后,发现其有一个属性是BeanA,故第二次去初始化实例化BeanA对象,此时创建BeanA的过程与第一次不同。因为第一次创建BeanA的时候,设置了BeanA对象为"正在创建",故此时能进入另一个处理逻辑------获取三级缓存的BeanFactory,通过这个来获取提前曝光的BeanA对象,接着删除二级缓存,这样拿到的BeanA对象虽然不完整,但足以依赖注入,BeanB对象也能完成实例化。然后BeanA对象也能顺利完成初始化,有种递归调用的感觉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值