spring 源码探索--单例bean解决循环依赖问题

spring 中循环依赖问题:

ItemA 依赖ItemB,ItemB依赖ItemC,ItemC依赖ItemA,这就造成了循环依赖。
循环依赖有两种实现方式:构造函数,setter注入

单例模式

  • 构造函数
public ItemA(ItemB itemB){
    this.itemB = itemB;
}

这种情况造成的循环依赖在spring中是无法解决的,只能报BeanCurrentlyInCreationException
在spring中,使用类的构造的函数实例化bean之前,即singletonObject = singletonFactory.getObject();调用这个之前,会判断是否当前的bean是否正在创建,如果当前bean正在创建,则会报异常。如果不正在创建,则加入创建bean的池中进行标记。

  • setter注入

即通过getter/setter方法注入bean.
这种情况可以解决,通过当实例化完bean,还没完成初始化时,提前暴露出一个ObjectFactory,这个工厂正好返回的是刚刚完成实例化的bean.

暴露ObjectFactory工厂

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);
        }
    });
}
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);
            }
        }
}

首先我们需要先了解几个bean的缓存池的作用
1. singletonObjects :BeanName和创建bean实例之间的关系(注:不是最终返回的bean,可能是FatoryBean)
2. singletonFactores: BeanName和创建bean的工厂
3. earlySingletonObjects: BeanName和创建bean实例之间的关系,与1有所不同,bean实例还处于创建的过程中,可以通过getBean获取,用于检测循环引用。(ps:这个缓存池与2是互斥的)
4. registeredSingletons:用来保存所有已经注册的bean.

到了这里,已经暴露了正在创建中的bean的ObjectFactory。
接下来就来到了初始化bean,注入相关的依赖
populateBean(beanName, mbd, instanceWrapper);

在这里面会注入依赖的bean,当有循环引用出现,一定有一个终止的条件才能解决循环依赖,就像递归一样。
在最开始加载bean的时候,spring首先从缓存中读取bean.

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

如果能在这几个缓存池拿到循环依赖的bean,那么循环依赖就解决了。
A -> B -> C -> A
假如一开始加载A对象,现在假如到了创建A的依赖C这个地方,那A现在的状态是刚刚完成构造函数的实例化,准备进行完成注入其他bean。这个时候创建他的ObjectFactory也已经加入到了singletonFactores,C这个时候去注入A,通过上面的getSingleton方法即可拿到刚刚实例化完的A对象。最后回到A对象,A继续完成其他属性的注入,此时C引用的A已经不再是刚刚实例化完的A对象了。整个依赖完成。

假设在上面的getSingleton方法上没有得到bean A,那异常BeanCurrentlyInCreationException会在再次创建bean A之前发生。
有一个正在创建bean池,会记录当前创建中的bean。

protected void beforeSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
}

原型模式

无法解决循环依赖,因为spring容器不缓存prototype作用域的bean.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值