文章目录
- 原理学习
- 源码溯源
本文参考:
画图带你彻底弄懂三级缓存和循环依赖的问题
Spring 三级缓存解决bean循环依赖,为何用三级缓存而非二级_笑矣乎的博客-CSDN博客
Spring为何需要三级缓存解决循环依赖,而不是二级缓存?_石杉的架构笔记的博客-CSDN博客
原理学习
主要的三级缓存工作机理学习参考 画图带你彻底弄懂三级缓存和循环依赖的问题
文章中解决了“是什么”的问题,这里主要梳理下”为什么“的问题,即为什么非得是三级缓存才能解决呢?也就是上面积累下来的问题
七、不用三级缓存,用二级缓存能不能解决循环依赖
遇到这种面试题,你就跟面试官说,如果行的话,Spring的作者为什么不这么写呢?
哈哈,开个玩笑,接下来说说到底为什么不行。
这里我先说一下前面没提到的细节,那就是通过ObjectFactory获取的Bean可能是两种类型,第一种就是实例化阶段创建出来的对象,还是一种就是实例化阶段创建出来的对象的代理对象。至于是不是代理对象,取决于你的配置,如果添加了事务注解又或是自定义aop切面,那就需要代理。这里你不用担心,如果这里获取的是代理对象,那么最后完全创建好的对象也是代理对象,ObjectFactory获取的对象和最终完全创建好的还是同一个,不是同一个肯定会报错,所以上面的理论依然符合,这里只是更加的细节化。
有了这个知识点之后,我们就来谈一下为什么要三级缓存。
第一级缓存,也就是缓存完全创建好的Bean的缓存,这个缓存肯定是需要的,因为单例的Bean只能创建一次,那么肯定需要第一级缓存存储这些对象,如果有需要,直接从第一级缓存返回。那么如果只能有二级缓存的话,就只能舍弃第二级或者第三级缓存。
假设舍弃第三级缓存
舍弃第三级缓存,也就是没有ObjectFactory,那么就需要往第二缓存放入早期的Bean,那么是放没有代理的Bean还是被代理的Bean呢?
1)如果直接往二级缓存添加没有被代理的Bean,那么可能注入给其它对象的Bean跟最后最后完全生成的Bean是不一样的,因为最后生成的是代理对象,这肯定是不允许的;
2)那么如果直接往二级缓存添加一个代理Bean呢?
- 假设没有循环依赖,提前暴露了代理对象,那么如果跟最后创建好的不一样,那么项目启动就会报错,
- 假设没有循环依赖,使用了ObjectFactory,那么就不会提前暴露了代理对象,到最后生成的对象是什么就是什么,就不会报错,
- 如果有循环依赖,不论怎样都会提前暴露代理对象,那么如果跟最后创建好的不一样,那么项目启动就会报错
通过上面分析,如果没有循环依赖,使用ObjectFactory,就减少了提前暴露代理对象的可能性,从而减少报错的可能。
假设舍弃第二级缓存
假设舍弃第二级缓存,也就是没有存放早期的Bean的缓存,其实肯定也不行。上面说过,ObjectFactory其实获取的对象可能是代理的对象,那么如果每次都通过ObjectFactory获取代理对象,那么每次都重新创建一个代理对象,这肯定也是不允许的。
从上面分析,知道为什么不能使用二级缓存了吧,第三级缓存就是为了避免过早地创建代理对象,从而避免没有循环依赖过早暴露代理对象产生的问题,而第二级缓存就是防止多次创建代理对象,导致对象不同。
源码溯源
为了从源码角度解决“不用三级缓存,用二级缓存能不能解决循环依赖”的问题,继续去找各种文章去解答:
参考 Spring 三级缓存解决bean循环依赖,为何用三级缓存而非二级_笑矣乎的博客-CSDN博客
DefaultSingletonBeanRegistry.java中有三级缓存,以及如何获取的源码
/**
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
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;
}
AbstractBeanFactory下面有一个createBean()的中心方法,其核心实现方法doCreateBean(),可以看出来是在类实例化以后,属性填充之前,先检查是否允许提前暴露,如果允许则将其加入三级缓存工厂中
文章作者认为二级缓存不能解决问题,主要是因为在AOP的场景下,注入到其他bean的,不是AOP代理对象,而是原始对象。但其实这也不完全对,完全可以让AOP代理对象注入到二级缓存中,但是这有悖于AOP的设计原理。AOP就是要等Bean实例化,属性填充,初始化完成之后才去 生成AOP代理对象的,如果使用二级缓存,又要将AOP代理对象注入的话,就需要强行将AOP的代理工作提前到早期暴露实例之前。
但是上面总归还是理论性的知识,在源码层面上,三级缓存究竟做了什么还需要探究,参考
Spring为何需要三级缓存解决循环依赖,而不是二级缓存?_石杉的架构笔记的博客-CSDN博客
三级缓存中key-value加入的是一个beanName 和一个ObjectFactory的工厂类
/**
* Add the given singleton factory for building the specified singleton
* if necessary.
* <p>To be called for eager registration of singletons, e.g. to be able to
* resolve circular references.
* @param beanName the name of the bean
* @param singletonFactory the factory for the singleton object
*/
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方法
// 若二级缓存不存在,查询三级缓存
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
最终调用getEarlyBeanReference方法
/**
* Obtain a reference for early access to the specified bean,
* typically for the purpose of resolving a circular reference.
* @param beanName the name of the bean (for error handling purposes)
* @param mbd the merged bean definition for the bean
* @param bean the raw bean instance
* @return the object to expose as bean reference
*/
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;
}
从getSingleton()中可以看出,在AOP的情况下,每次singletonFactory.getObject()取出来的都是不同的Aservice代理类,由于Aservice是单例的,这样子显然不行。因此将singletonFactory.getObject()取出来的东西放入二级缓存中,key还是beanName,这样子别的Bean在取的时候直接就可以从二级缓存中拿出来,不必每次调用singletonFactory.getObject()产生一个新的代理对象。
总而言之,在没有AOP的情况下,确实可以通过两级缓存解决循环依赖问题,但是如果加上AOP,两级缓存就不行了。
最后再复习下CGLib动态代理的用法:
package com.jxz.AopTest;
import net.sf.cglib.proxy.Enhancer;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/9/16
*/
public class CGlibTest {
public static void main(String[] args) {
WeakService weakService = new WeakService();
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(weakService.getClass().getClassLoader());
enhancer.setSuperclass(weakService.getClass());
enhancer.setCallback(new JXZMethodInterceptor());
// 创建代理类
WeakService proxy = (WeakService)enhancer.create();
proxy.f("I'm jxz");
}
}
package com.jxz.AopTest;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/9/16
*/
public class JXZMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("增强前...");
// 这里调用父类方法,不然一直调用本方法死循环了
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("增强后...");
return result;
}
}