Spring Bean缓存
singletonObjects
,一级缓存,存储的是所有创建好了的单例BeanearlySingletonObjects
,完成实例化,但是还未进行属性注入及初始化的对象singletonFactories
,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象
spring在对象getBean()时,先从一级缓存拿,拿到直接返回,拿不到就去二级缓存拿,再拿不到就去三级缓存拿ObjectFactory,拿到了就调用getObject创建对象,拿不到就返回null
一级缓存会在对象真正创建完成可以使用的时候put进去
循环依赖
循环依赖是什么?
简单来讲
假设A存在变量B,B存在变量A,当Spring首次实例化A对象时,在new A()之后需要为A的B属性赋值,此时需要再new B(),new B()之后又需要为A进行赋值,导致了无限循环的问题,这就是经典的循环依赖
循环依赖是如何解决的
假设情景A依赖于B,B依赖于A
首先A通过getBean调到doCreateBean(Spring通过getBean方法获取bean,如果不太清楚源码的可以看之前的文章),初步实例化后,属性注入之前进入到下边代码中
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
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));
}
复制代码
属性注入之前Spring将Bean包装成一个工厂添加进了三级缓存中
// 这里传入的参数也是一个lambda表达式,() -> 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);
}
}
}
复制代码
这里只是添加了一个工厂(通过这个工厂(ObjectFactory
)的getObject
方法可以得到一个对象),执行工厂的getObject相当于执行getEarlyBeanReference
。那么,什么时候会去调用这个工厂的getObject
方法呢?
当A开始属性注入,B开始实例化时,同样会通过getBean,同样会跟A一样属性注入,再次实例化A
此时再次实例化A时,getBean经过getSingleton会从三级缓存中拿出ObjectFactory
,调用getObject
会拿到A的对象
到此为止,循环依赖被解决
就这?
循环依赖确实被解决了,但是从过程看,似乎ObjectFactory缓存没什么存在的必要?似乎singletonObjects
和earlySingletonObjects
已经可以解决依赖问题了?
那么三级缓存到底有什么作用呢?我们再仔细分析下singletonFactories
当会进入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;
}
复制代码
它实际上就是调用了后置处理器的getEarlyBeanReference
,而真正实现了这个方法的后置处理器只有一个,就是通过@EnableAspectJAutoProxy
注解导入的AnnotationAwareAspectJAutoProxyCreator
。也就是说如果在不考虑AOP
的情况下,上面的代码等价于:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
return exposedObject;
}
复制代码
也就是说这个工厂啥都没干,直接将实例化阶段创建的对象返回了!所以说在不考虑AOP
的情况下三级缓存有用嘛?讲道理,真的没什么用
结合了AOP的循环依赖
之前我们已经说过了,在普通的循环依赖的情况下,三级缓存没有任何作用。三级缓存实际上跟Spring中的AOP
相关,我们再来看一看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;
}
复制代码
如果在开启AOP
的情况下,那么就是调用到AnnotationAwareAspectJAutoProxyCreator
的父类的AbstractAutoProxyCreator
的getEarlyBeanReference
方法,对应的源码如下:
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
// 如果需要代理,返回一个代理对象,不需要代理,直接返回当前传入的这个bean对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
复制代码
回到上面的例子,我们对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的生命周期本身就是通过AnnotationAwareAspectJAutoProxyCreator
这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization
方法中对初始化后的Bean完成AOP
代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。