Spring循环依赖冤冤相报何时了

Posted 洪宏鸿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring循环依赖冤冤相报何时了相关的知识,希望对你有一定的参考价值。

Spring循环依赖 🚎

梦想不会逃跑,会逃跑的永远都是自己

什么是循环依赖?

// A依赖了B
class A
public B b;


// B依赖了A
class B
public A a;

那么循环依赖是个问题吗?

如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情。

但是spring中bean是有自己的一套生命周期, 正因为Bean的生命周期所以才会出现循环依赖问题。当然,在Spring中,出现循环依赖的场景很多,有的场景Spring自动帮我们解决了(单例setter),而有的场景(构造器)则需要程序员来解决

如果对Bean的生命周期不甚了解, 可以先了解一下, 不然往下看怕是有点难受

单例setter, spring是如何解决的

下面说一下单例setter, spring是如何解决的

spring中通过三级缓存进行解决.

三级缓存的作用可以大概看一下, 大概记得有这么三个缓存先, 不急不急

三级缓存是通用的叫法。

  • 一级缓存为:singletonObjects

    • singletonObjects中缓存的是已经经历了完整生命周期的bean对象
  • 二级缓存为:earlySingletonObjects

    • earlySingletonObjects比singletonObjects多了一个early,表示缓存的是早期的bean对象。早期是什么意思?表示Bean的生命周期还没走完就把这个Bean放入了earlySingletonObjects
  • 三级缓存为:singletonFactories

    • singletonFactories中缓存的是ObjectFactory,表示对象工厂,表示用来创建早期bean对象的工厂

为什么需要三级缓存

原本声明周期中就有一个缓存 singletonObjects 存放我们已经初始化后的对象, 有aop对象, 也有普通对象.

既然

单单只有两级缓存行不行

我先说一下我的思路, 有两个类A和B

首先我用一个缓存, 就叫它 singletonFactories 吧

缓存我们实例化后填充属性之前的原始对象

A 放进 singletonFactories 了

发现依赖 B, 实例化 B, 放进 singletonFactories,

好了发现依赖 A, 从 singletonFactories 拿到,

B 继续初始化, 完事

普通的依赖对象没问题, 可AOP呢, 我AOP怎么办

二级缓存表示不服, 我实例化后就直接创建AOP对象

但是有一个问题, 那就是不是所有类都需要AOP呀

这是后置处理器(初始化过程一环)干的活, 怎么让我一个干实例化的干初始化的活

我还要实例化的时候判断是否循环依赖了, 这倒是可以使用一个集合存放正在创建的类来判断

在这里创建AOP倒是有一个好处, 我可以直接拿到原始对象

PS: 动态代理需要用到原始对象, 当然Cglib也需要

既然太靠前了, 那我能不能等到真正等到真正循环依赖的时候就再进行AOP呢?

好了, 二个缓存都搞不定

还得是你三级缓存

AOP, 我三级缓存来救你了

OK, 我现在增加一个缓存, 叫做 earlySingletonObjects, 用来存放还没完全填充属性和初始化好的对象, 通常是普通对象也可以存放AOP对象

好, 继续说说 A 和 B

A 示例化好后, 我把原始对象放到三级缓存中,

A 填充属性, 发现依赖 B,

B 实例化, 发现依赖 A,

分别从一级, 二级, 三级,如果拿到一级/二级就返回,

如果拿到三级, 那我就创建二级对象, 并且放到二级缓存,

有好奇的同学就要问了, 那我二级缓存也能创建 AOP 对象呀

当然可以, 但是这样就创建了多个 AOP 对象,

二级缓存之后, 就算有多个对象依赖 A, 我们也可以拿到同一个 AOP 代理对象,

而不是多个

其实 Spring 第三级缓存里面放的不是原始的实例化好的对象,

而是 ObjectFactory

通过 lambda 表达式进行创建

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

PS: 该方法会去执行SmartInstantiationAwareBeanPostProcessor中的getEarlyBeanReference方法,而这个接口下的实现类中只有两个类实现了这个方法,一个是AbstractAutoProxyCreator(AOP代理),一个是InstantiationAwareBeanPostProcessorAdapter

这样我们拿到 ObjectFactory 就能拿到当初的原始对象进行创建代理对象

AnnotationAwareAspectJAutoProxyCreator的父类就是AbstractAutoProxyCreator

AOP: 谢谢你, 我活下来了

ObjectFactory

使用ObjectFactory的方式不仅可以避免循环依赖问题,还可以为我们提供更多自定义实例化逻辑的机会。例如,我们可以使用ObjectFactory来创建代理对象、注入特定的依赖、在创建Bean实例之前或之后执行一些操作等等。

这里使用 ObjectFactory 而不直接把原始对象放置到三级缓存中, 是让我们可以做一些初始化的动作, 我们可以实现 SmartInstantiationAwareBeanPostProcessor
可以查看我的另一篇文章 spring5.1+SmartInstantiationAwareBeanPostProcessor 解决循环依赖_洪宏鸿的博客-CSDN博客

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) 
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) 
            for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) 
                exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
            
        
        return exposedObject;
    

三级缓存解决依赖循环全解

首先,singletonFactories中存的是某个beanName对应的ObjectFactory,在bean的生命周期中,生成完原始对象 之后,就会构造一个ObjectFactory存入singletonFactories中。这个ObjectFactory是一个函数式接口,所以支持 Lambda表达式:()-> getEarlyBeanReference(beanName, mbd,bean)

上面的Lambda表达式就是一个ObjectFactory,执行该Lambda表达式就会去执行getEarlyBeanReference方法,而 该方法如下:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) 
	Object exposedObject = bean;
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) 
		for (BeanPostProcessor bp : getBeanPostProcessors()) 
			if (bp instanceof SmartInstantiationAwareBeanPostProcessor) 
				SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
			
		
	
	return exposedObject;

该方法会去执行SmartInstantiationAwareBeanPostProcessor中的

getEarlyBeanReference方法,而这个接口下的实现类中只有两个类实现了这个方法,一个是AbstractAutoProxyCreator,一个是

InstantiationAwareBeanPostProcessorAdapter,它的实现如下:

// InstantiationAwareBeanPostProcessorAdapter
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException 
	return bean;

// AbstractAutoProxyCreator
@Override
public Object getEarlyBeanReference(Object bean, String beanName) 
	Object cacheKey = getCacheKey(bean.getClass(), beanName);
	this.earlyProxyReferences.put(cacheKey, bean);
	return wrapIfNecessary(bean, beanName, cacheKey);

在整个Spring中,默认就只有AbstractAutoProxyCreator真正意义上实现了 getEarlyBeanReference方法,而该类就是用来进行AOP的。上文提到的

AnnotationAwareAspectJAutoProxyCreator的父类就是AbstractAutoProxyCreator。 那么getEarlyBeanReference方法到底在干什么?

首先得到一个cachekey,cachekey就是beanName。

然后把beanName和bean(这是原始对象)存入earlyProxyReferences中 调用wraplfNecessary进行AOP,得到一个代理对象。

那么,什么时候会调用getEarlyBeanReference方法呢?回到循环依赖的场景中

左边文字:

这个ObjectFactory就是上文说的labmda表达式,中间有getEarlyBeanReference方法,注意存入 singletonFactories时并不会执行lambda表达式,也就是不会执行getEarlyBeanReference方法

右边文字:

从singletonFactories根据beanName得到一个ObjectFactory,然后执行

ObjectFactory,也就是执行getEarlyBeanReference方法,此时会得到一个A原始对象 经过AOP之后的代理对象,然后把该代理对象放入earlySingletonObjects中,注意此时并没有把代理对象放入singletonObjects中,那什么时候放入到singletonObjects中 呢?

我们这个时候得来理解一下earlySingletonObjects的作用,此时,我们只得到了A原始对象的代理对象,这个对象还不完整,因为A原始对象还没有进行属性填充,所以此时不能直接把A的代理对象放入singletonObjects中,所以只能把代理对象放入

earlySingletonObjects,假设现在有其他对象依赖了A,那么则可以从

earlySingletonObjects中得到A原始对象的代理对象了,并且是A的同一个代理对象。

当B创建完了之后,A继续进行生命周期,而A在完成属性注入后,会按照它本身的逻辑去进行AOP,而此时我们知道A原始对象已经经历过了AOP,所以对于A本身而言,不会再去进行AOP了,那么怎么判断一个对象是否经历过了AOP呢?会利用上文提到的earlyProxyReferences,在AbstractAutoProxyCreator的

postProcessAfterlnitialization方法中,会去判断当前beanName是否在

earlyProxyReferences,如果在则表示已经提前进行过AOP了,无需再次进行AOP。

对于A而言,进行了AOP的判断后,以及BeanPostProcessor的执行之后,就需要把A对应的对象放入singletonObjects中了,但是我们知道,应该是要把A的代理对象放入

singletonObjects中,所以此时需要从earlySingletonObjects中得到代理对象,然后入 singletonObjects中。

Spring Boot源码:循环依赖

循环依赖

以及

spring是如何解决循环依赖的

循环依赖

通俗来说

就是beanA中依赖了beanB,beanB中也依赖了beanA。

spring是支持循环依赖的,但是默认只支持单例的循环依赖,如果bean中依赖了原型bean,则需要加上lookup方法。

继续之前的项目,改造了People,User类:

@Component
public class People {
    @Autowired
    private User user;
    public People(){
        System.out.println("create People");
    }
}
@Component
public class User {
    @Autowired
    private People people;
    public User(){
        System.out.println("create User");
    }
}

People--(依赖)-->User--(依赖)-->People--(依赖)-->User.......,这就形成了循环依赖。

继续进入上篇的preInstantiateSingletons()方法:

 

 进行条件断点:

 

 

 

 进入上篇说的getSingleTon()方法:

 

 singletonObjects是spring的单例池,从其中没有拿到bean,singletonObject为null,且isSingletonCurrentlyInCreation()为false。

public boolean isSingletonCurrentlyInCreation(String beanName) {
        return this.singletonsCurrentlyInCreation.contains(beanName);
    }

其实这个方法就是去判断该map(singletonCurrentlyInCreation)是否包含该bean?(这里是people)。

这个方法通俗意思就是 “该bean是否在创建过程中?”

先不管这个意思,继续往下走。

spring创建对象前还会做很多判断,例如是否为原型,是否抽象?

 

 这里的getSingleton(String beanName, ObjectFactory<?> singletonFactory)是重载,与之前的getSingleton(String beanName, boolean allowEarlyReference)不同。

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 {
                    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) {
                    addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }

 

 

 

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

此方法把 “people”放入singletonsCurrentlyInCreation中,那么isSingletonCurrentlyInCreation("people")这时为true

进入lambda表达式 createBean()

create People Object ----注入依赖发现依赖User--->create User Object---发现依赖People---create People Object---发现isSingletonCurrentlyInCreation(beanName)为true,表示正在创建People:

 

 然后才会执行里面的代码:

 

 allowEarlyReference 是否允许从singletonFactories中通过getObject拿到对象,也就是执行if中的方法。

我们看一下singletonObjects(一级缓存),earlySingletonObjects(二级缓存),singletonFactories(三级缓存)。

AbstractAutowireCapableBeanFactory#doCreateBean()中:

 

 

 

 这时候存进去的并不是完整的bean:

 

 但是可以供User依赖注入了,这也就是spring对循环依赖的解决。

 

以上是关于Spring循环依赖冤冤相报何时了的主要内容,如果未能解决你的问题,请参考以下文章

Spring 获取单例流程

0源码基础学习Spring源码系列——Spring如何解决循环依赖

Spring Boot源码:循环依赖

Spring的循环依赖问题

Spring的循环依赖原理解析

手撕Spring源码,详细理解Spring循环依赖及三级缓存