Java OpenResty Spring Spring Boot MySQL Redis MongoDB PostgreSQL Linux Android Nginx 面试 小程序 Arthas JVM AQS juc Kubernetes Docker DevOps


Spring 循环依赖能否用二级缓存解决

Spring 面试 大约 8082 字

结论

不能。

原因

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;
    }
}
阅读 923 · 发布于 2021-12-23

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批