返回

Spring5——三级缓存和循环依赖

发布时间:2023-01-11 00:13:47 259
# html# java# spring# java# 信息

一、Spring 三级缓存

1、三级缓存的定义

看源码的DefaultSingletonBeanRegistry中有三个Map对象,通常情况下,称singletonObjects为一级缓存,earlySingletonObjects为二级缓存,singletonFactories 为三级缓存。

等级 名称 说明
一级 singletonObjects 可以理解为单例池
二级 earlySingletonObjects 早期单例对象缓存
三级 singletonFactories 单例工厂缓存
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

	/** Cache of singleton objects: bean name to bean instance. */
	//一级缓存:单例对象缓存池,beanName->Bean,其中存储的就是实例化,属性赋值成功之后的单例对象
	private final Map singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name to ObjectFactory. */
	//三级缓存:单例工厂的缓存,beanName->ObjectFactory,添加进去的时候实例还未具备属性
	// 用于保存beanName和创建bean的工厂之间的关系map,单例Bean在创建之初过早的暴露出去的Factory,
	// 为什么采用工厂方式,是因为有些Bean是需要被代理的,总不能把代理前的暴露出去那就毫无意义了
	private final Map> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name to bean instance. */
	//二级缓存:早期的单例对象,beanName->Bean,其中存储的是实例化之后,属性未赋值的单例对象
	// 执行了工厂方法生产出来的Bean,bean被放进去之后,
	// 那么当bean在创建过程中,就可以通过getBean方法获取到
	private final Map earlySingletonObjects = new HashMap<>(16);、
	
	/** Names of beans that are currently in creation. */
	//三级缓存是用来解决循环依赖,而这个缓存就是用来检测是否存在循环依赖的
	private final Set singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));	
	
}

2、三级缓存的作用

名称 作用
singletonObjects 存放初始化后的单例对象,也就是完成的bean对象
earlySingletonObjects 存放实例化,未完成初始化的单例对象(未完成属性注入的对象),也是用来解决性能问题
singletonFactories 存放ObjectFactory对象,存放的是工厂对象,主要用来解决循环依赖和aop的问题

3、为什么需要第三级缓存?

实际上使用二级缓存就能够实现循环依赖的解决,将二级缓存用来存放早期单例对象(半成品),然后一样的走生命周期的流程,也没有问题,为什么还需要三级缓存?

 

这个问题反映了spring设计的相当的精妙,如果说,注入的对象实现了aop功能,那么通过循环依赖注入其他bean的时候,bean是原始对象,也就是说没有经过aop的处理,不是最终的代理对象,需要通过三级缓存的ObjectFactory对象工厂才能解决aop的问题,才能获得最终需要的代理对象。

二、循环依赖是如何解决的

假设情景A依赖于B,B依赖于A

 

首先A通过getBean调到doCreateBean(Spring通过getBean方法获取bean),初步实例化即调用createBeanInstance()方法后,属性注入之前进入到下边代码中

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {
		
	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		//省略前面的部分代码

		
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		// 7.判断是否需要提早曝光实例:单例 && 允许循环依赖 && 当前bean正在创建中
		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");
			}
			// 8.提前曝光beanName的ObjectFactory,用于解决循环引用
			// 8.1 应用后置处理器SmartInstantiationAwareBeanPostProcessor,允许返回指定bean的早期引用,若没有则直接返回bean
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		// 初始化bean实例。
		Object exposedObject = bean;
		try {
			// 9.对bean进行属性填充;其中,可能存在依赖于其他bean的属性,则会递归初始化依赖的bean实例
			populateBean(beanName, mbd, instanceWrapper);
			// 10.对bean进行初始化
			//初始化bean,过程如下:
			//1:判断是否实现了BeanNameAware,BeanClassLoaderAware,
			//   BeanFactoryAware方法,如果有,则设置相关的属性
			//2: 调用bean初始化的前置(BeanPostProcessor)操作
			//3: 执行初始化的方法。
			//	如果有initializingBean,则调用afterPropertiesSet
			//	如果有InitMethod,则调用初始方法
			//4: 调用bean初始化的后置(BeanPostProcessor)操作
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		
		
		
		//省略中间的部分代码
		
		
		
		// 13.完成创建并返回
		return exposedObject;
	}
}

属性注入之前Spring将Bean包装成一个工厂添加进了三级缓存中,即调用addSingletonFactory()方法

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

	// 这里传入的参数也是一个lambda表达式,() -> getEarlyBeanReference(beanName, mbd, bean)
	protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			// 1.如果beanName不存在于singletonObjects缓存中
			if (!this.singletonObjects.containsKey(beanName)) {
				//往三级缓存里添加
				// 2.将beanName和singletonFactory注册到singletonFactories缓存(beanName -> 该beanName的单例工厂)
				this.singletonFactories.put(beanName, singletonFactory);
				//清除此Bean在二级缓存里的缓存信息
				// 3.移除earlySingletonObjects缓存中的beanName(beanName -> beanName的早期单例对象)
				this.earlySingletonObjects.remove(beanName);
				//这里为了记录注册单例的顺序
				// 4.将beanName注册到registeredSingletons缓存(已经注册的单例集合)
				this.registeredSingletons.add(beanName);
			}
		}
	}
}

这里只是添加了一个工厂(通过这个工厂(ObjectFactory)的getObject方法可以得到一个对象),执行工厂的getObject相当于执行getEarlyBeanReference。那么,什么时候会去调用这个工厂的getObject方法呢?

 

当A开始属性注入,B开始实例化时,同样会通过getBean,同样会跟A一样属性注入,再次实例化A,此时再次实例化A时,getBean经过getSingleton会从三级缓存中拿出ObjectFactory,调用getObject会拿到A的对象。

 

到此为止,循环依赖被解决。

 

解决循环依赖后,会在doCreateBean()方法中调用populateBean()方法填充属性,完成对bean的依赖属性进行注入(@Autowired)。

 

即在AbstractBeanFactory类中:

  • 通过 getBean(String name)方法、调用doGetBean()方法、在doGetBean()方法内部调用Object sharedInstance = getSingleton(beanName);代码,在getSingleton()方法中会从三级缓存中拿出ObjectFactory

  • 通过ObjectFactory单例对象工厂创建的单例对象,放到早期单例对象缓存中即二级缓存earlySingletonObjects,并在三级缓存中移除beanName对应的单例对象工厂

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

	//一级缓存:单例对象缓存池,beanName->Bean,其中存储的就是实例化,属性赋值成功之后的单例对象
	private final Map singletonObjects = new ConcurrentHashMap<>(256);

	//三级缓存:单例工厂的缓存,beanName->ObjectFactory,添加进去的时候实例还未具备属性
	// 用于保存beanName和创建bean的工厂之间的关系map,单例Bean在创建之初过早的暴露出去的Factory,
	// 为什么采用工厂方式,是因为有些Bean是需要被代理的,总不能把代理前的暴露出去那就毫无意义了
	private final Map> singletonFactories = new HashMap<>(16);

	//二级缓存:早期的单例对象,beanName->Bean,其中存储的是实例化之后,属性未赋值的单例对象
	// 执行了工厂方法生产出来的Bean,bean被放进去之后,
	// 那么当bean在创建过程中,就可以通过getBean方法获取到
	private final Map earlySingletonObjects = new HashMap<>(16);

	//三级缓存是用来解决循环依赖,而这个缓存就是用来检测是否存在循环依赖的
	private final Set singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));

	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// 1.从单例对象缓存中获取beanName对应的单例对象
		Object singletonObject = this.singletonObjects.get(beanName);
		// 2.如果单例对象缓存中没有,并且该beanName对应的单例bean正在创建中
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			//尝试给一级缓存对象加锁,因为接下来就要对缓存对象操作了
			synchronized (this.singletonObjects) {
				// 4.从早期单例对象缓存中获取单例对象(之所称成为早期单例对象,是因为earlySingletonObjects里
				// 的对象的都是通过提前曝光的ObjectFactory创建出来的,还未进行属性填充等操作)
				//尝试从二级缓存earlySingletonObjects这个存储还没进行属性添加操作的Bean实例缓存中获取
				singletonObject = this.earlySingletonObjects.get(beanName);
				// 5.如果在早期单例对象缓存中也没有,并且允许创建早期单例对象引用
				//如果还没有获取到并且第二个参数为true,为true则表示bean允许被循环引用
				if (singletonObject == null && allowEarlyReference) {
					// 6.从单例工厂缓存中获取beanName的单例工厂
					//从三级缓存singletonFactories这个ObjectFactory实例的缓存里尝试获取创建此Bean的单例工厂实例
					ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
					//如果获取到工厂实例
					if (singletonFactory != null) {
						// 7.如果存在单例对象工厂,则通过工厂创建一个单例对象
						singletonObject = singletonFactory.getObject();
						// 8.将通过单例对象工厂创建的单例对象,放到早期单例对象缓存中
						this.earlySingletonObjects.put(beanName, singletonObject);
						// 9.移除该beanName对应的单例对象工厂,因为该单例工厂已经创建了一个实例对象,并且放到earlySingletonObjects缓存了,
						// 因此,后续获取beanName的单例对象,可以通过earlySingletonObjects缓存拿到,不需要在用到该单例工厂
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		// 10.返回单例对象
		return singletonObject;
	}
}

循环依赖确实被解决了,但是从过程看,似乎ObjectFactory缓存没什么存在的必要?似乎singletonObjects和earlySingletonObjects已经可以解决依赖问题了?

 

那么三级缓存到底有什么作用呢?我们再仔细分析下singletonFactories

 

在doCreateBean()方法调用addSingletonFactory()方法,提前曝光beanName的ObjectFactory,用于解决循环引用的时候,会调用getEarlyBeanReference()方法,即addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));方法的时候

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {
		
	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		// 1.如果bean不为空 && mbd不是合成 && 存在InstantiationAwareBeanPostProcessors
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				// 2.应用所有SmartInstantiationAwareBeanPostProcessor,调用getEarlyBeanReference方法
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					// 3.允许SmartInstantiationAwareBeanPostProcessor返回指定bean的早期引用
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		// 4.返回要作为bean引用公开的对象,如果没有SmartInstantiationAwareBeanPostProcessor修改,则返回的是入参的bean对象本身
		return exposedObject;
	}
}

它实际上就是调用了后置处理器的getEarlyBeanReference,而真正实现了这个方法的后置处理器只有一个,就是通过@EnableAspectJAutoProxy注解导入的AnnotationAwareAspectJAutoProxyCreator。也就是说如果在不考虑AOP的情况下,上面的代码等价于:

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {
		
	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
	
		return exposedObject;
	}
}

也就是说这个工厂啥都没干,直接将实例化阶段创建的对象返回了!所以说在不考虑AOP的情况下三级缓存有用嘛?讲道理,真的没什么用。

结合了AOP的循环依赖

上面已经说过了,在普通的循环依赖的情况下,使用二级缓存也能解决循环依赖,那么三级缓存就显得可有可无了。其实、三级缓存实际上跟Spring中的AOP相关,我们再来看一看getEarlyBeanReference的代码:

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {
		
	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		// 1.如果bean不为空 && mbd不是合成 && 存在InstantiationAwareBeanPostProcessors
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				// 2.应用所有SmartInstantiationAwareBeanPostProcessor,调用getEarlyBeanReference方法
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					// 3.允许SmartInstantiationAwareBeanPostProcessor返回指定bean的早期引用
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		// 4.返回要作为bean引用公开的对象,如果没有SmartInstantiationAwareBeanPostProcessor修改,则返回的是入参的bean对象本身
		return exposedObject;
	}
}

如果在开启AOP的情况下,那么就是调用到AnnotationAwareAspectJAutoProxyCreator的父类的AbstractAutoProxyCreator的getEarlyBeanReference方法,对应的源码如下:

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
		implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
		
	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyProxyReferences.put(cacheKey, bean);
		// 如果需要代理,返回一个代理对象,不需要代理,直接返回当前传入的这个bean对象
		return wrapIfNecessary(bean, beanName, cacheKey);
	}
}

wrapIfNecessary为Spring实现Bean代理的核心方法

  • wrapIfNecessary在两处会被调用,一处是getEarlyBeanReference,另一处是postProcessAfterInitialization
  • 在wrapIfNecessary方法内部调用getAdvicesAndAdvisorsForBean()返回匹配当前Bean的所有Advice\Advisor\Interceptor,用于判断此该类是否需要创建代理。
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
		implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
		
	/**
	 * Spring实现Bean代理的核心方法。wrapIfNecessary在两处会被调用,一处是getEarlyBeanReference,
	 * 另一处是postProcessAfterInitialization
	 */
	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		//已经被处理过
		// 1.判断当前bean是否在targetSourcedBeans缓存中存在(已经处理过),如果存在,则直接返回当前bean
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		//不需要被织入逻辑的
		// 2.在advisedBeans缓存中存在,并且value为false,则代表无需处理
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		//是不是基础的bean 是不是需要跳过的
		// 3.bean的类是aop基础设施类 || bean应该跳过,则标记为无需处理,并返回
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// Create proxy if we have advice.
		// 返回匹配当前Bean的所有Advice\Advisor\Interceptor
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		// 5.如果存在增强器则创建代理
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			//创建Bean对应的代理,SingletonTargetSource用于封装实现类的信息
			// 5.1 创建代理对象:这边SingletonTargetSource的target属性存放的就是我们原来的bean实例(也就是被代理对象),
			// 用于最后增加逻辑执行完毕后,通过反射执行我们真正的方法时使用(method.invoke(bean, args))
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			// 5.2 创建完代理后,将cacheKey -> 代理类的class放到缓存
			this.proxyTypes.put(cacheKey, proxy.getClass());
			// 返回代理对象
			return proxy;
		}
		//该Bean是不需要进行代理的,下次就不需要重复生成了
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}
}

回到上面的例子,我们对A进行了AOP代理的话,那么此时getEarlyBeanReference将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建后的对象。

 

三级缓存的意义何在?

 

就以我们上的A、B为例,其中A被AOP代理,我们先分析下使用了三级缓存的情况下,A、B的创建流程

假设不使用三级缓存,直接用二级缓存中,也是可以实现的,那么三级缓存搭配二级缓存的存在有什么意义呢?

上面两个流程的唯一区别在于为A对象创建代理的时机不同,在使用了三级缓存的情况下为A创建代理的时机是在B中需要注入A的时候,而不使用三级缓存的话在A实例化后就需要马上为A创建代理然后放入到二级缓存中去。对于整个A、B的创建过程而言,消耗的时间是一样的(所以常见的三级缓存提高了效率这种说法都是错误的)

 

上述这种情况下,差别就是在哪里创建代理。如果不用三级缓存,使用二级缓存,违背了Spring在结合AOP跟Bean的生命周期的设计!Spring结合AOP跟Bean的生命周期本身就是通过AbstractAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。

Spring Bean的生命周期:https://blog.51cto.com/u_14014612/5995566

 

参考: https://blog.csdn.net/qq_38762237/article/details/108468333

https://juejin.cn/post/6930904292958142478#heading-5

https://www.cnblogs.com/juniorMa/p/14087517.html

特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报
评论区(0)
按点赞数排序
用户头像
精选文章
thumb 中国研究员首次曝光美国国安局顶级后门—“方程式组织”
thumb 俄乌线上战争,网络攻击弥漫着数字硝烟
thumb 从网络安全角度了解俄罗斯入侵乌克兰的相关事件时间线