Spring如何循环依赖

一、什么是循环依赖

循环依赖指的是2个或2个以上的bean互相持有对方,从而形成闭环的依赖场景。例如A依赖B,B依赖C,而C又依赖A,这种情况就叫做循环依赖。

二、循环依赖的场景

1、构造器的循环依赖

2、setter循环循环依赖

三、Spring Bean的创建过程

源码见org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)

@Override
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

Spring的单例对象的初始化分为三步

  • createBeanInstance实例化:即通过构造器反射出对象实例,此时属于半成品,因为内部的属性都未初始化
  • populateBean填充属性:对内部依赖的属性进行填充,如果依赖另外的bean,则递归创建另外的bean并填充到自己的属性上
  • InitializeBean初始化:调用bean中形如initMethodInitializingBean等方法

四、Spring如何解决循环依赖

1、构造器的循环依赖

根据上面bean的创建过程的分析,第一步必须反射获取bean的实例,其中反射的方式是通过构造器反射,如果构造器存在循环依赖,那么必须要查找到对应的bean才能进行实例化。例如A依赖B,B依赖C,C依赖A,A实例化需要B,则挂起A去创建B,B实例化需要C,则挂起B去创建C,C实例化需要A,而A还处于创建中,甚至还未实例化出来,因此流程死锁,无法继续执行。

2、setter循环依赖

Spring可以解决setter情况下的循环依赖

2.1、三级缓存

Spring为了解决循环依赖,创建bean的原则是不等bean创建完成就将创建bean的ObjectFactory提早曝光到缓存中,一旦下一个bean创建时候需要时直接使用ObjectFactory。这里涉及到三级缓存

  • 一级缓存singletonObjects:单例对象的cache,存放可用的成品bean
  • 二级缓存earlySingletonObjects:第二级缓存,存放半成品的Bean半成品的Bean是已创建对象,但是未注入属性和初始化。用以解决循环依赖
  • 三级缓存singletonFactories:第三级缓存,存的是Bean工厂对象,用来生成半成品的Bean并放入到二级缓存中。用以解决循环依赖

2.2、实现细节

具体方法位于org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //检查缓存中是否存在实例
    Object singletonObject = this.singletonObjects.get(beanName);
    //如果为空且singletonsCurrentlyInCreation存在该bean
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        //锁定singletonObjects全局变量
        synchronized (this.singletonObjects) {
            //从earlySingletonObjects获取bean
            singletonObject = this.earlySingletonObjects.get(beanName);
            //如果获取不到且允许早期依赖
            if (singletonObject == null && allowEarlyReference) {
                //从singletonFactories获取ObjectFactory,这是因为有时候某些方法需要提前初始化的时候会
                //调用addSingletonFactory方法将对应的ObjectFactory初始化策略存储在singletonFactories
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                //如果不为空
                if (singletonFactory != null) {
                    //调用预先设定的getObject方法
                    singletonObject = singletonFactory.getObject();
                    //记录在缓存中
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    //earlySingletonObjects与singletonFactories是互斥的,因此这里需要remove
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

上述方法执行流程如下

  • 首先从一级缓存singletonObjects中获取,如果获取到则直接返回,
  • 如果获取不到且对象也不是正在创建中,则直接返回null表示缓存中没有
  • 如果对象是正在创建中,则去到二级缓存earlySingletonObjects中进行查找,如果获取到直接返回
  • 如果获取不到且传递不允许早期依赖,直接返回null
  • 如果允许早期依赖,则进一步到三级缓存singletonFactories中查找
  • 如果未查询到,直接返回null
  • 如果查询到,则调用ObjectFactorygetObject方法获取对象,将对象放入二级缓存中,同时移除三级缓存。

当bean反射实例化之后,如果当前bean是单例,且允许循环依赖,且在处在创建的过程中,则会讲当前bean放到三级缓存中,目的是提前暴露,这是解决循环依赖的关键,源码位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

//满足以下三个条件才会调用方法addSingletonFactory
//(1)单例
//(2)允许循环依赖
//(3)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");
    }
    
//getEarlyBeanReference目的是调用SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference()这个方法
//也就是给调用者个机会,自己去实现暴露这个bean的应用的逻辑
//例如可以实现一些AOP的逻辑,生成代理对象
//如果不是SmartInstantiationAwareBeanPostProcessor,则什么也不做    
    
//添加bean的创建工厂到缓存中,目的是提前暴露,解决循环依赖
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

addSingletonFactory方法逻辑如下,目的是将objectFactory放入到三级缓存singletonFactories中,同时移除二级缓存earlySingletonObjects

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);
        }
    }
}

2.3、流程图解

以A依赖B,B依赖C,C依赖A为例

图解Spring循环依赖(无代理情况)

2.4、扩展

  • 为什么需要包装一层ObjectFactory对象?

    如果创建的Bean有对应的代理,那其他对象注入时,注入的应该是对应的代理对象;但是Spring无法提前知道这个对象是不是有循环依赖的情况,而正常情况下(没有循环依赖情况),Spring都是在创建好完成品Bean之后才创建对应的代理。这时候Spring有两个选择

    1. 不管有没有循环依赖,都提前创建好代理对象,并将代理对象放入缓存,出现循环依赖时,其他对象直接就可以取到代理对象并注入。
    2. 不提前创建好代理对象,在出现循环依赖被其他对象注入时,才实时生成代理对象。这样在没有循环依赖的情况下,Bean就可以按着Spring设计原则的步骤来创建

    Spring选择了第二种方式,即在对象外面包一层ObjectFactory,提前曝光的是ObjectFactory对象,在被注入时才在ObjectFactory.getObject方式内实时生成代理对象,并将生成好的代理对象放入到第二级缓存Map<String, Object> earlySingletonObjects,为了防止对象在后面的初始化(init)时重复代理,在创建代理时,earlyProxyReferences缓存会记录已代理的对象、

五、相关问题

1、AOP下的循环依赖

以@Transactional为例

1.1、bean代理对象创建过程

a) bean原始对象生成

bean创建先构造器反射出一个对象(空对象,内部属性尚未组装),然后封装为一个ObjectFactory,而这个只有当产生了循环依赖才会被调用

源码位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

//满足以下三个条件才会调用方法addSingletonFactory
//(1)单例
//(2)允许循环依赖
//(3)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");
    }
    //添加bean的创建工厂到缓存中,目的是提前暴露,解决循环依赖
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

其中getEarlyBeanReference方法会返回代理对象

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    //这个方法的目的是调用SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference()这个方法
    //也就是给调用者个机会,自己去实现暴露这个bean的应用的逻辑
    //例如可以实现一些AOP的逻辑,生成代理对象
    //如果不是SmartInstantiationAwareBeanPostProcessor,则什么也不做

    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            //SmartInstantiationAwareBeanPostProcessor处理
            //对于注解@Transactional,存在InfrastructureAdvisorAutoProxyCreator
            //它是SmartInstantiationAwareBeanPostProcessor类型的BeanPostProcessor
            //因此会走getEarlyBeanReference逻辑生成代理对象
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

对于注解@Transactional,会存在InfrastructureAdvisorAutoProxyCreator,它是SmartInstantiationAwareBeanPostProcessor类型的BeanPostProcessor,因此会走执行getEarlyBeanReference逻辑生成代理对象。查看ibp.getEarlyBeanReference(exposedObject, beanName)逻辑,源码位于org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference

@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    //先放入earlyProxyReferences缓存中
    //earlyProxyReferences存放已生成的代理对象
    this.earlyProxyReferences.put(cacheKey, bean);
    //生成代理对象
    return wrapIfNecessary(bean, beanName, cacheKey);
}

再看wrapIfNecessary方法

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    //已经处理过的bean直接返回
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    //如果无需增强,直接返回
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    //如果是基础设施类或者配置了无需自动代理,则直接返回
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // Create proxy if we have advice.
    //获取当前bean内需要增强的方法
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    //如果存在需要增强的方法,则创建代理类
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        //创建代理类
        Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    //如果无需代理,则设置为false,直接返回原始类型引用
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

如果bean无需代理,则直接返回原始类型,否则获取需要代理的方法生成代理对象

b) populateBean属性填充

这里没有什么特殊地方

c) initializeBean调用初始化方法

源码位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        //对特殊的bean处理:Aware、BeanClassLoaderAware、BeanFactoryAware
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        //调用后置处理器的applyBeanPostProcessorsBeforeInitialization方法
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        //激活用户自定义的init方法
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        //调用后置处理器的applyBeanPostProcessorsAfterInitialization方法
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

这里主要做了以下几件事

  • Aware接口处理,可以获取对应的资源,即如果bean实现了类似BeanClassLoaderAwareBeanFactoryAware接口,需要组装相应的属性
  • 调用BeanPostProcessorpostProcessBeforeInitialization方法
  • 调用用户自定义的init方法
  • 调用BeanPostProcessorpostProcessAfterInitialization方法

这里重点看下第四步,即执行BeanPostProcessorpostProcessAfterInitialization方法

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    //实例化后的后置处理器处理
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

前面我们说到容器中会存在InfrastructureAdvisorAutoProxyCreator这个BeanPostProcessor,因此我们看下它的postProcessAfterInitialization方法逻辑,源码位于org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        //根据给定的bean的class和name构建出一个key
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        //这里很重要
        //如果之前生成的bean与当前bean不是一个或者从未生成过,则需要生成代理对象并返回
        //否则直接返回原始bean
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            //如果当前bean适合被代理,则进行封装
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

这里判断了this.earlyProxyReferences.remove(cacheKey) != bean,当为true时,表示earlyProxyReferences不存在该bean或者存在与当前bean不是一个。而我们之前由于循环依赖已经生成了代理bean,且放入到earlyProxyReferences中 ,由于此时bean是原始类型,非代理类型,则该条件不满足,直接返回原始类型bean。

1.2、循环依赖检查

源码位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

//满足以下三个条件才earlySingletonExposure才会为true
//(1)单例
//(2)允许循环依赖
//(3)bean正在创建中
if (earlySingletonExposure) {
    //从二级缓存中获取当前bean
    //注意此时的参数allowEarlyReference是false,表示不会去三级缓存里面再去调用一次getObject了,而是在一二级获取bean
    Object earlySingletonReference = getSingleton(beanName, false);
    //如果获取的bean没有变化,表示没有被代理过,后面一切正常
    if (earlySingletonReference != null) {
        if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
        }
        //allowRawInjectionDespiteWrapping 标注是否允许此Bean的原始类型被注入到其它Bean里面,即使自己最终会被包装(代理)
        //默认是false,即不允许
        //如果allowRawInjectionDespiteWrapping为false且当前bean存在被依赖的bean
        //此处为二级缓存的bean与exposedObject不相同,因此需要找到当前bean所被依赖的bean,
        //一旦存在则说明其他bean依赖了当前bean的二级缓存的引用,而非最终bean的引用,所以肯定存在问题
        else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            //获取当前bean依赖的bean集合
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
                if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                    actualDependentBeans.add(dependentBean);
                }
            }
            //一旦存在,直接抛出异常
            if (!actualDependentBeans.isEmpty()) {
                throw new BeanCurrentlyInCreationException(beanName,
                                                           "Bean with name '" + beanName + "' has been injected into other beans [" +
                                                           StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                                                           "] in its raw version as part of a circular reference, but has eventually been " +
                                                           "wrapped. This means that said other beans do not use the final version of the " +
                                                           "bean. This is often the result of over-eager type matching - consider using " +
                                                           "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
            }
        }
    }
}

该段代码是循环依赖的校验(当earlySingletonExposuretrue才进行校验),此时bean为原始对象,exposedObject为初始化结束后返回的对象,校验逻辑如下

  • 调用getSingleton方法从缓存中再获取一次bean

    注意此时传递的参数allowEarlyReference是false,表示不会去三级缓存里面再去调用一次getObject了,而是在一二级获取bean。根据上面的分析,此时二级缓存存在,所以返回的earlySingletonReference不为空

  • 校验exposedObject == bean

    bean存放的是原始对象,而exposedObject 根据前面的分析也是原始对象,二者相等,因此将二级缓存取出的代理代码赋值给exposedObject ,即是最终生成的对象,流程结束。

因此注入容器内的bean是代理对象,被依赖的bean注入的属性也代理对象,完美解决AOP代理下的循环依赖。

2、@Async下的循环依赖问题

2.1、异常原理

当使用@EnableAsync时,会注入AsyncAnnotationBeanPostProcessor这个后置处理器,而它只在postProcessAfterInitialization方法中实现了代理,即在bean初始化方法执行完成后才会生成代理,因此构造函数实例化后生成的ObjectFactory,调用它的getObject方法返回的不是代理对象,而是原始对象,因此上面的循环依赖检查代码中,exposedObject将是进行过代理的对象,非原始对象,exposedObject == bean将返回false,表示bean在执行方法initializeBean时发生了变化,所以需要检查一下是否把原来的半成品给暴露出去了,一旦发现,则直接抛出异常。

2.2、与@Transactional事务注解区别

  • @Transactional使用的是自动代理创建器AbstractAutoProxyCreator,上篇文章详细描述了,它实现了getEarlyBeanReference()方法从而很好的对循环依赖提供了支持
  • @Async的代理创建使用的是AsyncAnnotationBeanPostProcessor单独的后置处理器实现的,它只在一处postProcessAfterInitialization()实现了对代理对象的创建,因此若出现它被循环依赖了,就会抛出异常。

2.3、如何解决

根据上面的分析,解决方案如下四种(以【A是被@Async修饰的类,B依赖A,A又依赖B】为例)

  1. allowRawInjectionDespiteWrapping设置为true(不推荐)

    @Component
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowRawInjectionDespiteWrapping(true);
        }
    }
    

    allowRawInjectionDespiteWrapping表示是否允许包装代理类的原始注入,设置为true表示允许,上面的检查依赖就不会执行,也就不会抛出异常,但是这样会导致B将依赖的是A的原始对象,而不是代理类型,从而导致B没法使用A异步方法的异步特性,因此非常不推荐。

  2. 使用@Lazy修饰(推荐)

    在B类上的A属性上加上@Lazy,表示延迟加载,这样容器启动不会保持,B依赖的也是A的代理类,A在容器内也是个代理类。但是有一点需要注意,B内持有的A和容器中的A不是同一个代理类。该方式建议。

  3. @ComponentScan(lazyInit = true)解决

    原理同上,但是作用范围太广,容易误伤。

  4. 不要让@Async的Bean参与循环依赖

    实际业务开发很难避免,该方案需要具体情况具体对待。

参考资料

https://blog.csdn.net/f641385712/article/details/92801300

https://blog.csdn.net/f641385712/article/details/92797058

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值