Spring 循环依赖能否用二级缓存解决
Spring 面试 About 8,082 words结论
不能。
原因
bean
创建分为三步:实例化、填充属性、初始化。
循环依赖发生在填充属性阶段,代理对象的创建在初始化完成后的后置处理器回调中。
如果只用二级缓存,那么在属性填充阶段直接注入的是原始对象,那么在初始化阶段可能变成了代理对象,那么就导致了引用不一致。
第三级缓存的作用是一个钩子函数,当有AOP
代理时,ObjectFactory
工厂类生产的是代理对象,没有AOP
时返回的原始对象。
备注
Spring Boot 2.6.0
默认不允许循环依赖。
可使用配置开启:
spring.main.allow-circular-references=true
三级缓存
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
// 一级缓存,存放初始化完成的单例 bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存,解决循环依赖时,从三级缓存中获取单例对象或代理对象,临时放入到二级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 三级缓存,保存 ObjectFactory lambda,循环依赖时通过 getEarlyBeanReference 方法获取代理对象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}
getSingleton
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 先从一级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);
// 如果为 null 并且正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 从二级缓存中获取
singletonObject = this.earlySingletonObjects.get(beanName);
// 二级缓存为 null 并且允许提早引用
if (singletonObject == null && allowEarlyReference) {
// 加锁避免多线程问题
synchronized (this.singletonObjects) {
// 多线程环境,再次从一级缓存获取
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) {
// 调用 ObjectFactory 方法获取到 bean 或代理对象
singletonObject = singletonFactory.getObject();
// 将获取到的对象放入二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
// 移除三级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
}
doCreateBean
bean
实例化、填充属性、初始化都在doCreateBean
方法中完成。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
// 实例化 bean 对象,包装到 BeanWrapper 对象中
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 判断是否允许提早暴露未完成初始化的 bean
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 如果允许提早暴露
// 加入到三级缓存中,value 是 ObjectFactory 类型的,包装的是 getEarlyBeanReference 方法
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
// 填充属性,@Autowired 等成员变量在此注入
populateBean(beanName, mbd, instanceWrapper);
// 初始化 bean 完成
// initializeBean 中会调用代理 AnnotationAwareAspectJAutoProxyCreator 的 postProcessAfterInitialization 创建代理对象并返回
// 如果已经在循环依赖提前从三级缓存 getEarlyBeanReference 创建了代理对象,则代理类在创建时判断是否已经在提前引用阶段创建过了,创建过了直接返回原始对象,没有创建过则创建代理对象
// 所以 initializeBean 返回有两种情况
// 1. 有循环依赖,提前引用阶段创建了代理了,则返回原始对象
// 2. 没有循环依赖,没有调用提前引用方法,则返回代理对象
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
// ...
}
// 为什么需要三级缓存最关键的在这里!!!
if (earlySingletonExposure) {
// 因为 initializeBean 有两种情况,这里 getSingleton 也有两种情况
// 1. 有循环依赖,三级缓存获取到了代理对象放入到二级缓存中了
// 此时 earlySingletonReference 是代理对象,bean 是原始对象,exposedObject 是原始对象
// 将 代理对象赋值给 exposedObject,所以如果只用二级缓存无法解决有代理对象的情况
// 2. 没有循环依赖,二级缓存中没有对象,不走 if 逻辑(此处 false 不获取三级缓存)
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
}
}
return exposedObject;
}
// 获取提早的 bean 引用
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
// 调用 AOP 中的后置处理器 AnnotationAwareAspectJAutoProxyCreator 中的方法
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
// 通知实现了 Aware 接口的方法
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 通知 postProcessBeforeInitialization 方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 调用 init-method
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
// ...
}
if (mbd == null || !mbd.isSynthetic()) {
// 通知 postProcessAfterInitialization 方法
// 正是在此步创建的代理对象
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
}
AnnotationAwareAspectJAutoProxyCreator
祖先类AbstractAutoProxyCreator
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);
// 循环依赖 getSingleton 二级缓存没有时会从三级缓存取
// 三级缓存的 ObjectFactory getObject 回调到代理类的 getEarlyBeanReference 方法
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 将 bean 信息放入到 map 中
// 表示已经在循环依赖中提前创建出来了代理对象
this.earlyProxyReferences.put(cacheKey, bean);
// 包装成代理类返回
return wrapIfNecessary(bean, beanName, cacheKey);
}
// initializeBean 中会调用此方法
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 从表示提前创建出的代理对象集合中移除
// 如果存在且相同,直接不进入 if,直接返回原始的未被代理的 bean 对象
// 如果不存在,则包装成代理类
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
}
Views: 3,306 · Posted: 2021-12-23
————        END        ————
Give me a Star, Thanks:)
https://github.com/fendoudebb/LiteNote扫描下方二维码关注公众号和小程序↓↓↓
Loading...