spring循环依赖--三级缓存

什么是循环依赖?

就是字面意思,AService依赖BService,BService依赖AService。
如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情。
但是在spring中,循环依赖就是一个问题了,因为一个对象并不是简单 new出来的,而是会经过一系列bean的生命周期,所以会出现循环依赖。有些循环spring可以帮我们解决,有些依赖需要程序员自己解决。我们先说前者,循环依赖的从生命周期说起。


Bean的生命周期

这里只会描述一下生命周期的大概过程:

  1. Spring扫描class得到BeanDefinition,根据class推断构造方法,然后根据beanDefinition和构造方法,实例化得到原始bean;
  2. 填充原始对象中的属性(依赖注⼊)
  3. 如果原始对象中的某个⽅法被AOP了,那么则需要根据原始对象⽣成⼀个代理对象
  4. 把最终⽣成的代理对象放⼊单例池(源码中叫做singletonObjects)中,下次getBean时就直接从单例
    池拿即可

属性填充,其实就是依赖注入,所以出现循环依赖,主要也是在第二步。


详解循环依赖

示例:

public class BService{
	@Autowired
	private AService aservice;
}

public class AService{
	@Autowired
	private BService bservice;
}

解决循环依赖,需要三级缓存
一级:singletonObject map<beanName,Object> 单例池 保证对象单例
二级:earlySingletonObject map<beanName,Object> 保证对象单例 也是对象 只不过是一个不完整的对象
三级:singletonFactories Map<String, ObjectFactory<?>> 原始对象池

step one:

AService的Bean的生命周期

1class->实例化得到AService的原始对象
2、给BService属性赋值->从单例池找BService->找不到->创建BService的bean

	BService的生命周期
	2.1class->实例化得到BService的原始对象
	2.2、给AService属性赋值->从单例池找AService->找不到->又去创建AService对象=========这样就又返回去了,形成了死循环
	2.3、给其他属性赋值
	2.4、其余事情
    2.5、将对象放入单例池

3、给其他属性赋值
4、其余事情(aop等)
5、将对象放入单例池

在2.2步的时候,AService创建时—>需要BService—>BService创建时—>需要A,这样就产生了死循环。
在1步实例化后得到的对象,下文都称为 原始对象。

step two:使用第三级缓存,解决初步问题。

AService的Bean的生命周期

1class->实例化得到AService的原始对象->放入三级缓存map<beanName,AService原始对象>
2、给BService属性赋值->从单例池找BService->找不到->创建BService的bean

	BService的生命周期
	2.1class->实例化得到BService的原始对象->放入三级缓存map<beanName,BService原始对象>
	2.2、给AService属性赋值->从单例池找AService->找不到->从A三级缓存map->找到AService原始对象
	2.3、给其他属性赋值
	2.4、其余事情(aop等)
  2.5、将对象放入单例池

3、给其他属性赋值
4、其余事情(aop等)
5、将对象放入单例池

通过一个 A原始对象缓存map 就可以解决问题 得以生命周期进行完;
但是依然还有问题,如果AService的方法配置了AOP,那么AOP会在第四步进行,aop后得到一个 代理对象 放入了单例池;但是在BService生命周期中,是赋值的AService的原始对象,那这样,就不一致了;

step three:解决对象一致问题–利用aop提前进行

如果AService配置了切面,那么在2.2步应该赋值一个代理对象。。。
但是在2.2步如何得到一个代理对象呢?是否可以提前进行aop?貌似可以。。。

1class->实例化得到AService的原始对象-> 如何检测? -> 提前进行aop -> 得到AService的代理对象-> 放入三级缓存map<beanName,AService代理对象>
2、给BService属性赋值->从单例池找BService->找不到->创建BService的bean

	BService的生命周期
	2.1class->实例化得到BService的原始对象->放入三级缓存map<beanName,BService原始对象>
	2.2、给AService属性赋值->从单例池找AService->找不到->从A三级缓存map获取对象->得到AService代理对象
	2.3、给其他属性赋值
	2.4、其余事情
    2.5、将对象放入单例池

但是 提前进行aop是有前提的,前提就是只有AService出现了循环依赖的情况下,才可以提前,正常都是第四步才aop,如何检测出现了循环依赖呢?

step four:如何检测 AService 出现了循环依赖呢?

如果在1处判断,会费老鼻子劲,其实在2.2步会很容易。
解决方法:在生命周期第一步之前 新加一步 加一个标志,既可以处理了;

0、creatingSet.add('aService')
1class->实例化得到AService的原始对象
2、给BService属性赋值->从单例池找BService->找不到->创建BService的bean

	BService的生命周期
	2.1class->实例化得到BService的原始对象
	2.2、给AService属性赋值->从单例池找AService->找不到->creatingSet?->AService出现了循环依赖-> 提前AOP -> 生成AService代理对象
    …………
3、给其他属性赋值
4、其余事情(aop等)
5、将对象放入单例池
6、creatingSet.remove('aService')

这样还是有问题的,如果aService还注入了cService,那么在第2步,还需要进行cService的生命周期,到2.2的时候,还会进行一次aService的aop,生成两个AService的代理对象,这样就出问题了,因为是单例bean,应该就一个。

step five:二级缓存–如果防止出现多个bean

这时候就引入了 第二级 缓存 earlySingletonObject map<beanName,Object>

0、creatingSet.add('aService')
1class->实例化得到AService的原始对象
2、给BService属性赋值->从单例池找BService->找不到->创建BService的bean

	BService的生命周期
	2.1class->实例化得到BService的原始对象
	2.2、给AService属性赋值->从单例池找AService->找不到->creatingSet是否有值->AService出现了循环依赖-> 从earlySingletonObject找AService -> 找不到 -> 提前AOP -> 生成AService代理对象(不完整)->放入earlySingletonObject
  ……………………

  CService的生命周期
	2.1class->实例化得到CService的原始对象
	2.2、给AService属性赋值->从单例池找AService->找不到->creatingSet?->AService出现了循环依赖-> 从earlySingletonObject找AService -> 得到AService代理对象(不完整)
  ……………………

3、给其他属性赋值
4、其余事情
4.5、从earlySingletonObject获取代理对象
5、将对象放入单例池

这样就保证了 AService代理对象(不完整) 只有一个,解决了 会出现 多个代理对象的问题 。
而且第四步、第五步之间增加一步,直接从earlySingletonObject获取代理对象,这时候获取到的就是经历了整个生命周期的bean, 然后放入代理池

step six:三级缓存的正确使用

在进行aop之前,需要用到第三季缓存,需要从里边拿到原始对象,以及beanName和beanDefinition
我们知道代理对象中一个target属性,target属性就指向原始对象
三级缓存 Map<String, ObjectFactory<?>> singletonFactories value实际上 是一个lambda表达式 lambda(beanName,beanDefiniton,bean)

0、creatingSet.add('aService')
1class->实例化得到AService的原始对象->放入三级缓存singletonFactories 
2、给BService属性赋值->从单例池找BService->找不到->创建BService的bean

	BService的生命周期
	2.1class->实例化得到BService的原始对象-> 第三级缓存map
	2.2、给AService属性赋值->从单例池找AService->找不到->creatingSet?->AService出现了循环依赖-> earlySingletonObject -> 找不到 -> 提前AOP(aop需要拿到aService的原始对象,就需要从三级缓存singletonFactories 拿) -> 生成AService代理对象(不完整)--放入二级缓存earlySinletonObject

step seven :如何标记aop提前进行

提前进行了aop,第四步就不用进行aop了,那么如何判断 进行过aop了呢?
两个关键方法:

  1. getEarlyBeanReference(): 第二步aop 执行lambda表达式的时候 调用
  2. postProcessAfterInitialzation():第四步的时候调用 初始化的方法
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
	//cacheKey 其实就是 bean的名字
	Object cacheKey = getCacheKey(bean.getClass(), beanName);
	//将cacheKey存入earlyProxyReferences,也是一个ConcurrentHashMap 就用这个map来判断是否进行过aop
	this.earlyProxyReferences.put(cacheKey, bean);
	//然后进行aop,得到一个代理对象,方法里的createProxy,就是aop的入口
	return wrapIfNecessary(bean, beanName, cacheKey);
}

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
	if (bean != null) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		//earlyProxyReferences中存的是哪些提前进行了aop的bean,beanName   aop之前的对象
		//注意 earlyProxyReferences 中并没有存aop之后的代理对象
		//就用这个map来判断是否进行过aop,如果没有提前进行aop,调remove方法得到一个null,null肯定不等object
		if (this.earlyProxyReferences.remove(cacheKey) != bean) {
			//没有提前进行aop,则进行aop
			return wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}

总结

到此,由于属性注入引起的循环依赖,spring解决原理就结束了,我们发现,spring在解决循环依赖的时候,不仅仅是靠三级缓存,还有两个东西。
creatingSet:标记是否产生循环
earlyProxyReferences:标记是否提前进行了aop

ConcurrentHashMap在step seven中已经出现,接下来,我们看看三级缓存和creatingSet的真实面貌,先看获取单例bean的方法:

//获取单例bean,singletonFactory是一个lambda表达式,
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) {
			…………………………
			//获取单例bean前,先做一个标记,正在创建
			beforeSingletonCreation(beanName);
			boolean newSingleton = false;
			boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
			if (recordSuppressedExceptions) {
				this.suppressedExceptions = new LinkedHashSet<>();
			}
			try {
				//创建单例bean,回调用createBean()
				singletonObject = singletonFactory.getObject();
				newSingleton = true;
			}
			catch (IllegalStateException ex) {
				…………………………
			}
			finally {
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = null;
				}
				//创建单例bean成功后,set移除bean
				afterSingletonCreation(beanName);
			}
			if (newSingleton) {
				//加入单例池
				addSingleton(beanName, singletonObject);
			}
		}
		return singletonObject;
	}
}

creatingSet
在获取单例bean时,有一个方法beforeSingletonCreation():

protected void beforeSingletonCreation(String beanName) {
		//inCreationCheckExclusions中的bean,表示如果是这些bean正在创建中,重复创建也没关系
		//singletonsCurrentlyInCreation中的bean,表示如果是这些bean正在创建中,在没创建完时不能重复创建
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

/** Names of beans that are currently in creation. */
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));

protected void afterSingletonCreation(String beanName) {
	if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
		throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
	}
}

singletonsCurrentlyInCreation就是我们的creatingSet,表示这些bean正在创建中,在没创建完时不能重复创建;
我们点进去beforeSingletonCreation可以看到,是一个set。创建前加到set;
点进去afterSingletonCreation可以看到,创建成功后移除;

三级缓存
我们在看加入单例池的方法addSingleton():

protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
		//一级缓存 存入bean
		this.singletonObjects.put(beanName, singletonObject);
		//三级缓存 移除
		this.singletonFactories.remove(beanName);
		//二级缓存 移除
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	}
}

/** Cache of singleton objects: bean name to bean instance. */
	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 HashMap<>(16);

在addSingleton()方法,可以看到三级缓存,一级缓存是ConcurrentHashMap,二级、三级是HashMap。
这是为啥呢?留着后边解释。
bean生命周期这篇博文里边,我们看到了doCreateBean()方法,这个方法里边还有一块代码,我们的细品下:

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");
	}
	//添加 三级缓存
	addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

Object exposedObject = bean;
try {
	//属性赋值和初始化
	populateBean(beanName, mbd, instanceWrapper);
	exposedObject = initializeBean(beanName, exposedObject, mbd);
……………………
if (earlySingletonExposure) {
    //从三个缓存池,挨个获取
	Object earlySingletonReference = getSingleton(beanName, false);
	………………………………
}

//添加 三级缓存
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);
		}
	}
}

//从三个缓存池,挨个获取
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	//先从一级缓存取
	Object singletonObject = this.singletonObjects.get(beanName);
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			//再从二级缓存取
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				//再从三级缓存取,如果有循环依赖,三级缓存应该是有的
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					//从三级缓存,获取到作为value值得lambda,执行aop,得到代理对象
					singletonObject = singletonFactory.getObject();
					//添加到二级缓存,从三级缓存移除
					this.earlySingletonObjects.put(beanName, singletonObject);
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}

这段代码,我们看到了:
原始对象添加到三级缓存,从二级缓存移除;
三级缓存中获取原始对象,添加到二级缓存,然后在移除三级中缓存;
我们看到,二级缓存和三级缓存,一个添加,另一个就要删除,他俩是同步进行的,是一个原子操作,即使是ConcurrentHashMap也无法保证两个map操作的原子行,所以添加和删除三级缓存都是在synchronized下运行的,这样的话,二级和三级缓存是hashMap就够了,不需要使用ConcurrentHashMap。

放到三级缓存后,立马移除二级缓存的该bean,这是为啥呢?
三级缓存中的lambda,只能使用一次,多次使用,会多次进行aop,那就产生了多个代理对象,这是不允许的。


什么样的循环依赖,spring解决不了

spring解决不了,构造函数的相互依赖, 也就是在对象的 原始对象 没有创建成功之前就依赖,解决不了,如下所示:

public class BService{
	private AService aservice;

	public BService(AService aService){
		this.aservcie = aService;
	}
}

public class AService{
	private BService bservice;

	public AService(BService bService){
		this.bservcie = bService;
	}
}

实例化AService,只有调用现在唯一的 构造方法AService(BService bService),但是必须得传一个参数BService的bean,才能调,那就得找到一个BService的bean,单例池没有。
那就的实例化BService,实例化BService,只有调用现在唯一的 构造方法BService(AService aService),但是必须得传一个参数AService的bean,才能调,但是AService的bean还没有创建出来,…又循环了……
类似这样的相互依赖 解决不了;

如果想解决的话,可以用@Lazy注解;
@Lazy注解的原理:实例化AService,先生成一个bService的代理对象,当调用bService方法的时候,会去单例池找,找不到再创建

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木子松的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值