Spring Bean 循环依赖解决简单分析

Spring Bean 循环依赖解决简单分析

本文Spring版本:

5.0.5.RELEASE

什么是循环依赖:

     循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A 依赖 B,B 又依赖 A;或者A依赖于B,B依赖于C,C又依赖于A。

Spring 循环依赖的处理方式:

①构造器的循环依赖:这种依赖spring是处理不了的,直 接抛出BeanCurrentlylnCreationException异常。

②单例模式下的setter循环依赖:通过“三级缓存”处理循环依赖。

③非单例循环依赖:无法处理。

 

Spring 如何解决单例Bean的循环依赖问题?

利用三级缓存(DefaultSingletonBeanRegistry中定义)。

singletonObjects

用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
earlySingletonObjects 存放原始的 bean 对象(尚未填充属性)
singletonFactories 存放 bean对应的工厂对象(ObjectFactory)

 

解决循环依赖加载bean的大致流程:

 

判断缓存中是否有X:

//DefaultSingletonBeanRegistry#getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //singletonObjects 第一级缓存,BeanFactory的单例全存在singletonObjects中
    //保存的是已经初始化完全的单例
    Object singletonObject = this.singletonObjects.get(beanName);
            //isSingletonCurrentlyInCreation=true,代表beanName所代表的bean循环依赖了
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            //第二级缓存,保存的是未初始化完全的单例(只是实例化)
            singletonObject = this.earlySingletonObjects.get(beanName);
            //allowEarlyReference在当前场景下,默认为true
            if (singletonObject == null && allowEarlyReference) {
                //第三级缓存,不是真的缓存,缓存的是生成二级缓存的工厂方法
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //通过缓存singletonFactories中的对象工厂获取bean的对象
                    //如果有AOP,获取的会是代理对象
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

实例化bean之后,会把获取当前bean的方式放入到singletonFactories缓存:

注意:Map<String, ObjectFactory<?>> singletonFactories

该级缓存存放的并不是实例化的bean,而是一个ObjectFactory对象,它可以构造出对应的bean

//AbstractAutowireCapableBeanFactory#doCreateBean
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    if (logger.isDebugEnabled()) {
        logger.debug("Eagerly caching bean '" + beanName +
                "' to allow for resolving potential circular references");
    }
    //添加缓存
    addSingletonFactory(beanName, new ObjectFactory<Object>() {
        @Override
        public Object getObject() throws BeansException {
            return getEarlyBeanReference(beanName, mbd, bean);
        }
    });
}

当bean实例化完成之后,会被放到一级缓存singletonObjects中,用于被其他bean获取:

//DefaultSingletonBeanRegistry#getSingleton#addSingleton
protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}

为什么是三级缓存?

      好像使用 earlySingletonObjects 和 singletonObjects 两级缓存,一个存放早期对象,一个存放初始化完成后的对象,也能实现同样的功能,singletonFactories 好像显得有些多此一举。其实不是的,对于普通对象,确实只要返回刚创建完的早期对象就好了,但对于内部有被 AOP 增强的方法的对象,需要返回的是代理对象。我们可以看一下 ObjectFactory 匿名内部类里面调用的 getEarlyBeanReference 方法:

//AbstractAutowireCapableBeanFactory
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            // SmartInstantiationAwareBeanPostProcessor 这个后置处理器会在返回早期对象时 
            // 被调用,如果返回的对象需要加强,那这里就会生成代理对象
            // 如果返回的对象需要AOP增强,那这里exposedObject 就会替换成代理对象的引用
         // 比如上图中的bean X 如果被AOP增强,那么bean Y 加载时获取的bean X 其实是X的代理对象 
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

我们可以看到对于一般的对象,返回的就是传入的早期对象,但是对于内部有被 AOP 增强的方法的对象,会使用后置处理器返回一个代理对象。

如果在解决循环依赖时生成了代理对象,那么 AbstractAutoProxyCreator 会把原对象放入一个 Map 中,这个 Map 的作用是防止同类对象被重复动态代理:

//AbstractAutoProxyCreator
public Object getEarlyBeanReference(Object bean, String beanName) {
// 提前暴露代理对象的引用,是在postProcessAfterInitialization之前执行
    Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            this.earlyProxyReferences.add(cacheKey);
        }

        return this.wrapIfNecessary(bean, beanName, cacheKey);
}
//AbstractAutoProxyCreator
//有些BeanPostProcessor提供对Bean的Wrap的操作,但是生命周期位于在set操作之后,避免重复代理
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
}

earlySingletonObjects作用是可以提升多次循环依赖加载的效率(singletonFactory.getObject() 不用多次执行),比如 X 依赖 Y, Y 依赖 X, Z 也依赖 X。Y 注入 X 时 earlySingletonObjects 已经通过 singletonFactory.getObject() 得到了原始X,这样加载Y时就不用再执行 singletonFactory.getObject() 构造 X 实例了,直接从earlySingletonObjects获取即可。

 

发布了41 篇原创文章 · 获赞 105 · 访问量 14万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章