好玩Spring之三级缓存及循环依赖的处理

Spring的三级缓存,伴随着bean的生命周期。

常见缓存

在DefaultSingletonBeanRegistry类中,有很多的cache。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

	// 一级缓存
	/** 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);

	/** Set of registered singletons, containing the bean names in registration order. */
	private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

	// 创建中的beanName的集合
	/** Names of beans that are currently in creation. */
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));

	// Bean被创建完成后,会标记为这个
	/** Names of beans that have already been created at least once. */
	private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
}

doGetBean(…)

之前,我们在将Bean的生命周期时,讲到Bean的生命周期是从如下代码开始的

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
	...
	// Create bean instance.
	// bean的生命周期,从这里正式开始
	if (mbd.isSingleton()) {
		sharedInstance = getSingleton(beanName, () -> {
			try {

				return createBean(beanName, mbd, args);
			}
			catch (BeansException ex) {
				// Explicitly remove instance from singleton cache: It might have been put there
				// eagerly by the creation process, to allow for circular reference resolution.
				// Also remove any beans that received a temporary reference to the bean.
				destroySingleton(beanName);
				throw ex;
			}
		});
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
	}
	...
}

getSingleton(…)

一、二、三级缓存的操作顺序如下:

在getSingleton(…)方法中,主要进行以下3件事情,从这个方法开始,也进行多个缓存的操作:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	...
	beforeSingletonCreation(beanName); // 00 bean标记为创建中
	try {
		singletonObject = singletonFactory.getObject(); // 01 执行createBean -> doCreateBean
		newSingleton = true;
	}
	catch (IllegalStateException ex) {
		...
	}
	finally {
		if (recordSuppressedExceptions) {
			this.suppressedExceptions = null;
		}
		afterSingletonCreation(beanName); // 02 bean创建完成,从singletonsCurrentlyInCreation中移除
	}
	if (newSingleton) {
		addSingleton(beanName, singletonObject);
	}
	...
}

三级缓存加入,二级缓存移除

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
	...
	// Eagerly cache singletons to be able to resolve circular references
	// even when triggered by lifecycle interfaces like BeanFactoryAware.
	// 是否需要提前曝光:单例 & 允许循环依赖 & 当前bean正在创建中
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
			isSingletonCurrentlyInCreation(beanName));
	if (earlySingletonExposure) {
		...
		// 加入三级缓存:为避免后期循环依赖,可以在bean初始化完成前将创建实例的ObjectFactory加入工厂
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}
	...
}
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);
		}
	}
}
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
	...
	if (earlySingletonExposure) {
		Object earlySingletonReference = getSingleton(beanName, false);
	}
	...
}

二级缓存加入,三级缓存移除

看getSingleton(…)方法,前面加入三级缓存后,这里会将对象从三级缓存移动到二级缓存

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) {
					singletonObject = singletonFactory.getObject();
					// 取到后,从三级缓存提前曝光到到二级缓存
					this.earlySingletonObjects.put(beanName, singletonObject);
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}

一级缓存加入,二三级缓存移除

待整个doCreate(…)方法执行完,便再次回到了开头讲的getSingleton(…)方法,在这个方法中,有这么一段代码代码

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	...
	if (newSingleton) {
		addSingleton(beanName, singletonObject);
	}
	...
}
protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
		// 一级缓存添加
		this.singletonObjects.put(beanName, singletonObject);
		// 二三级缓存移除
		this.singletonFactories.remove(beanName);
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	}
}

也就是说,在bean创建完成后(实例化、初始化完成后),最终会放入singletonObjects一级缓存。

总结

现在我们以例子来总结下,Spring借助三级缓存来解决循环依赖的过程
A --> B --> A

  1. doCreate(…)创建A对象(已实例化,未初始化)
  2. 将A放入三级缓存singletonFactories
  3. 调用populateBean(…)方法,A需要注入B,发现缓存里还没有B对象,开始创建B。
  4. doCreate(…)创建B对象(已实例化,未初始化)
  5. B对象需要注入A对象,此时从三级缓存取得A对象,同时从三级缓存移动到二级缓存
  6. B对象继续注入其他属性和初始化,之后将成品B对象放入一级缓存,同时删除二三级缓存。
  7. A对象获取单例B的引用完成属性注入
  8. A对象继续注入其他属性和初始化,之后将成品A对象放入一级缓存,同时删除二三级缓存。

Spring三级缓存不能解决的循环依赖问题

多例Bean循环依赖

单例Bean无法注入该多例Bean(非抽象、单例 并且非懒加载的类才能被提前初始bean)

构造器注入依赖

由于Bean的实例化需要构造器,而构造器执行又需要依赖的Bean,所以无法对Bean进行实例化,也就无法使用缓存机制从而报循环依赖错误
可使用@Lazy注解解决

单例注入代理对象依赖

由于代理对象和实际对象是两个对象,所以在生成完毕后Spring会将对象和二级缓存中对象比较,如果不一致则报循环依赖错误

DependsOn循环依赖

当Spring进行加载时,发现有@DependsOn依赖则会直接报错

有个视频不错:
spring为什么使用三级缓存而不是两级? - 李小奔的回答 - 知乎
https://www.zhihu.com/question/445446018/answer/2043081423

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值