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

  • Post author:
  • Post category:其他


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



什么是循环依赖?

// 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中。



版权声明:本文为HongZeng_CSDN原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。