Spring三级缓存与循环依赖

Spring循环依赖

什么是循环依赖

  • Spring中的对象创建的时候会去创建依赖的对象,如果这个时候依赖的对象创建的时候又依赖当前的对象这种现象称为循环依赖。本篇讨论如下两种依赖注入的方式
注入方式解决循环依赖
@Autowired解决
构造函数注入报错
  • @Autowired循环依赖的代码如下
// 组件A
@Component
public class ComponentA {

    @Autowired
    private ComponentB b;

    @PostConstruct
    public void postConstruct() {
        System.out.println("执行");
    }
}

// 组件B
@Component
public class ComponentB {

    @Autowired
    private ComponentA a;
}
  • 那么Spring是如何解决循环的依赖的呢?接下来先说说Spring的三级缓存

Spring三级缓存

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

	/** Cache of singleton objects: bean name to bean instance. */
	// 一级缓存Map,存放最终的Bean对象
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name to ObjectFactory. */
	// 三级缓存,构建对象的对象工厂
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name to bean instance. */
	// 二级缓存存放提前创建的对象,但是对象还没有完成初始化
	private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
  • 如上所示,一级缓存singletonObjects,二级缓存earlySingletonObjects,三级缓存singletonFactories。详细的作用在代码中已经解释。
  • 有的人可能要问了为什么要三级缓存呢?两级行不行?不急,这个问题后面再解释。先来捋一捋循环依赖的流程

循环依赖流程简析

  • 以上面的@Autowired流程为例,下面是ComponentAComponentB的对象创建栈
lambda$doGetBean$0:321, AbstractBeanFactory (org.springframework.beans.factory.support)2】
getObject:-1, 1325124186 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$12)2】
getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)2】
doGetBean:319, AbstractBeanFactory (org.springframework.beans.factory.support)2// 第二次调用方法创建依赖对象B
getBean:199, AbstractBeanFactory (org.springframework.beans.factory.support)2】
resolveCandidate:277, DependencyDescriptor (org.springframework.beans.factory.config)
doResolveDependency:1276, DefaultListableBeanFactory (org.springframework.beans.factory.support)
resolveDependency:1196, DefaultListableBeanFactory (org.springframework.beans.factory.support)
inject:595, AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement (org.springframework.beans.factory.annotation)
inject:90, InjectionMetadata (org.springframework.beans.factory.annotation)
// 填充属性的时候,调用 AutowiredAnnotationBeanPostProcessor 处理器来处理依赖Bean的注入
postProcessProperties:376, AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
populateBean:1402, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
// doCreateBean 585 创建三级缓存  addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
doCreateBean:591, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:514, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:321, AbstractBeanFactory (org.springframework.beans.factory.support)1】
getObject:-1, 1325124186 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$12)1// 创建对方发起的地方,对象创建完成之后放入一级缓存并删除一二级缓存中的数据
getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)1// 在 doGetBean 的247行 获取单例 Object sharedInstance = getSingleton(beanName); 这里如果是循环
// 如果对象工厂已经有创建对象,依赖则会出现先创建对象并放入二级缓存
doGetBean:319, AbstractBeanFactory (org.springframework.beans.factory.support)1// 第一次调用方法创建对象A
getBean:199, AbstractBeanFactory (org.springframework.beans.factory.support)1】
preInstantiateSingletons:866, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:878, AbstractApplicationContext (org.springframework.context.support)
refresh:550, AbstractApplicationContext (org.springframework.context.support)
<init>:89, AnnotationConfigApplicationContext (org.springframework.context.annotation)
main:12, CircleApplication (com.frank.spring.circledemo)
  • 结尾标注【1】表示第一次调用,【2】表示第二次调用也就是出现了递归调用。对象创建的流程是先New出对象,然后填充属性,最后完成对象的初始化
  • 从下往上看,Spring通过反射创建ComponentA对象后(第26行),为了支持循环依赖,(第16行)会向三级缓存singletonFactories中放入key=componentA,value=匿名函数(lambda表达式)addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))一个键值对
  • (第14行)接着来填充对象postProcessProperties:376, AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)执行到这里的时候会调用AutowiredAnnotationBeanPostProcessor处理器的方法来解决依赖的属性,最终走到了(第6行)getBean:199, AbstractBeanFactory (org.springframework.beans.factory.support) 【2】这个地方,也就是需要去创建ComponentB,则会重复刚刚ComponentA创建对象的逻辑
  • ComponentB创建的过程中又因为要依赖ComponentA,关键代码
protected <T> T doGetBean(
		String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
		throws BeansException {

	String beanName = transformedBeanName(name);
	Object bean;

	// Eagerly check singleton cache for manually registered singletons.
	// 这里回去三级缓存中获取真的的实例,
	Object sharedInstance = getSingleton(beanName);
	// 省略其它代码
	...
	return (T) bean;
}
  • 调用DefaultSingletonBeanRegistry.getSingleton的流程如下
// 创建ComponentB的时候依赖ComponentA时,只是创建了A实例和三级缓存中A的创建映射,其它一二级缓存都为空
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		// ComponentA此时还为空,一级
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				synchronized (this.singletonObjects) {
					// Consistent creation of early reference within full singleton lock
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
							// 最终的逻辑是走到这里,获取对象工厂来创建对象放入二级缓存并移除三级缓存的对象
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
								singletonObject = singletonFactory.getObject();
								this.earlySingletonObjects.put(beanName, singletonObject);
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}
  • 走完上面的流程ComponentB中已经注入了ComponentA的引用了,上述说道B重复了A的创建流程,所以此时在singletonFactories中也有B对象创建的lambda式, 接下来从DefaultSingletonBeanRegistry.getSingleton可以看出ComponentB已经进入了一级缓存并清空了B的二三级缓存数据(二级缓存没有B的数据) ,到此B的路程就走完了,ComponentA已经进入了二级缓存(在上面代码的第21行处进入二级缓存的),最后退出到(第13行)postProcessProperties:376, AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)这里继续创建A的逻辑。
DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory<?>)
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) {
				// 省略分支,只看主线。
				// 通过这个方法获取了对象
				try {
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				...
				// 把该实例放入以及缓存中并且删除二级、三级缓存中的数据
				if (newSingleton) {
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}
  • A继续执行AbstractAutowireCapableBeanFactory 的 initializeBean完成初始化,最后跟ComponentB类似把数据放入一级缓存,清空对应的A的二三级缓存数据,至此整个流程结束
  • 就上面的整个流程我准备了一张图,再来复盘下整个过程,下图的①②③④⑤是流程走向,三级缓存是最左边那列,ComponentAComponentB是对象。首先①首先创建ComponentA hashCode后面四位为2204,并把lambda放入三级缓存,hashCode后四位为2281,由于依赖了B,这个时候创建ComponentB(4086)对象并把并对象的真正创建逻辑放入三级缓存(4190),B依赖了A这个时候会走③,ComponentA会进入二级缓存并删除ComponentA的三级缓存2281的数据,流程接下来走到④,创建真正的ComponentB进入一级缓存并删掉B的三级缓存数据,这个时候只剩下A没有进入一级缓存了,ComponentB创建完成后流程走到⑤,CompnentA会进入一级缓存并删除③中二级缓存和①中三级缓存的数据,至此流程结束
    循环依赖
  • 以上流程就是没有AOP的循环依赖流程
AOP的循环的依赖
  • AOP的循环依赖流程跟上面的流程基本一致,只是创建最终对象的时候会调用AbstractAutowireCapableBeanFactory#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) {
				// AspectJAwareAdvisorAutoProxyCreator 类来完成对象的代理返回代理对象
				SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
	}
	return exposedObject;
}

循环依赖面试题目解析

  • 为什么要三级缓存?一级缓存会出现死循环的问题。
  • 二级缓存可以解决缓存依赖。假设还是上面的例子:创建A的时候直接把A放入二级缓存,由于依赖B,则要创建B,在创建B的时候,需要找A,会从二级缓存中获取A,依赖解决后把B放入一级缓存,接着继续创建A,B已经创建,直接引用B并删除A的二级缓存同时把A放入一级缓存中去,最终解决了循环依赖,一切非常完成有木有!!然而问题了,这个违背了Spring的基于开放的哲学逻辑。所有的操作在二级缓存就已经完成,不能进行很好的扩展了,
  • 在接口SmartInstantiationAwareBeanPostProcessor有下面一段描述

Extension of the InstantiationAwareBeanPostProcessor interface, adding a callback for predicting the eventual type of a processed bean.

  • 大概说InstantiationAwareBeanPostProcessor实现了这个接口的BeanPostProcessor会产生最终用的bean对象,就是说如果只有二级缓存的情况下大大的打折了Spring的扩展
  • 综上所述,三级缓存是解决循环依赖的最佳选择

构造函数循环依赖

  • 循环依赖的构造函数代码如下,两个对象ConstructAConstructB以及启动函数
@Component
public class ConstructA {

    private ConstructB b;

    public ConstructA(ConstructB b) {
        this.b = b;
    }
}

@Component
public class ConstructB {

    private ConstructA a;

    public ConstructB(ConstructA a) {
        this.a = a;
    }
}

// 启动类
@Configuration
@ComponentScan(basePackages = "com.frank.spring.circledemo")
public class CircleApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(CircleApplication.class);
    }
}
  • 启动报错
Error creating bean with name 'constructA': Requested bean is currently in creation: Is there an unresolvable circular reference?
  • 也就是说Spring没有解决构造函数的循环依赖问题,因为构造函数是在最先New出来的,这个是无法解决的

总结

  • 讨论循环依赖有个前提就是对象是单例模式的,否则不存在循环依赖
  • 循环依赖有很多种形式,这篇讨论的目前比较常用的两种方式,通过@Autowired的方式的已经解决了循环依赖,而通过构造函数没有解决循环依赖
  • 二级缓存可以解决循环依赖但是降低了框架的扩展性,所以三级缓存是最佳的选择(这里可以回忆下TCP为什么是三次握手,可以查看TCP三次握手和四次挥手,关联学习)

欢迎可以关注spring-cloud系列 openfeign hystrix打怪升级系列性能优化系列
试试右下角一键三连你会有惊奇的发现:)你的点赞和关注是我创作的最大动力,有什么不足和错误的地方欢迎留言!可以微信搜索关注【小二说码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值