Spring循环依赖问题

Spring中单例Bean实例创建的三个步骤:

  1. 创建对象,createBeanInstance

  2. 填充属性,populateBean

  3. 初始化操作(调用初始化方法),initializeBean

构造器循环依赖

无法解决,因为实例都还没有创建出来。比如A构造器依赖B,B构造器依赖A,在创建A实例需要B实例,创建B实例又需要A实例,所以构造器循环依赖无法解决。

setter方法循环依赖

setter方法循环依赖发生在第二步,解决的方法是,在实例化之后、填充属性之前将Bean实例放入缓存

缓存

// 一级缓存,存储创建完全成功的单例Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 二级缓存,在Bean实例化、依赖注入、初始化后,会把Bean实例从三级缓存转移到二级缓存
// 如果需要生成代理对象,这里已经是代理对象了
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

// 三级缓存,主要来解决循环依赖问题,把刚实例化的对象封装成ObjectFactory,然后放入三级缓存
// 为什么封装成ObjectFactory
// 因为用直接的实例对象的引用的话,如果该实例对象后面会生成代理对象,就没办法做替换
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
三级缓存

AbstractAutowireCapableBeanFactory#doCreateBean方法中会调用DefaultSingletonBeanRegistry#addSingletonFactory把刚实例化的Bean对象封装成ObjectFactory对象放到三级缓存

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
    // 省略...
    // ObjectFactory实例
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    // 省略...
}


protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        /**
         * 如果单例池当中不存在才会add
         * 因为如果bean存在单例池的话其实已经是一个完整的bean了
         * 所以如果这个对象已经是一个完整的bean,就不需要关心,不需要进入if
         * 如果一级缓存中没有改实例,则放入三级缓存中
         */
        if (!this.singletonObjects.containsKey(beanName)) {
            // 放入三级缓存
            this.singletonFactories.put(beanName, singletonFactory);
            /**
             * 从二级缓存中remove掉当前的bean
             * 这三个map当中其实其实存的都是一个对象,不能同时存在三个map
             */
            // 从二级缓存中移除
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

用Lambda表达式封装成一个ObjectFactory对象,调用ObjectFactory#getObject时会调用getEarlyBeanReference

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                // aop的AspectJAwareAdvisorAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessor接口
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

调用AspectJAwareAdvisorAutoProxyCreator的父类AbstractAutoProxyCreator#getEarlyBeanReference方法

public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    // 使用动态代理,产生代理对象
    return wrapIfNecessary(bean, beanName, cacheKey);
}
二级缓存

在以下两个场景,会把Bean实例从三级缓存转移到二级缓存:

  1. 在Bean实例化、依赖注入、初始化后,AbstractAutowireCapableBeanFactory#doCreateBean方法中,调用DefaultSingletonBeanRegistry#getSingleton
  2. 获取Bean实例,会先去缓存中获取,AbstractBeanFactory#doGetBean方法中,调用DefaultSingletonBeanRegistry#getSingleton

DefaultSingletonBeanRegistry#getSingleton,把Bean实例从三级缓存转移到二级缓存。如果需要生成代理对象,二级缓存这里已经是代理对象,有两点:

  1. 代理对象是在初始化后的AbstractAutoProxyCreator#postProcessAfterInitialization方法生成。

  2. 如果对象提前暴露给其他Bean,singletonFactory.getObject()会产生代理对象

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从一级缓存中获取单例对象
    Object singletonObject = this.singletonObjects.get(beanName);
    // isSingletonCurrentlyInCreation:判断当前单例bean是否正在创建中,也就是没有初始化完成
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 从二级缓存中获取单例bean
            singletonObject = this.earlySingletonObjects.get(beanName);
            // allowEarlyReference:是否允许从singletonFactories中通过getObject拿到对象
            if (singletonObject == null && allowEarlyReference) {
                // 从三级缓存中获取单例bean
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 通过单例工厂获取单例bean
                    // 如果需要创建代理对象,这里会生成代理对象,调用AbstractAutoProxyCreator#getEarlyBeanReference方法
                    singletonObject = singletonFactory.getObject();
                    // 从三级缓存移动到了二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}
一级缓存

在对Bean实例化、依赖注入、初始化之后,会将Bean实例保存到一级缓存。

DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory<?>)方法中调用DefaultSingletonBeanRegistry#addSingleton将Bean实例保存到一级缓存,删除在二级和三级对应的。一级缓存是真正对外暴露的Bean实例对象缓存。

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
	    // 一级缓存
	    this.singletonObjects.put(beanName, singletonObject);
	    // 三级缓存
	    this.singletonFactories.remove(beanName);
	    // 二级缓存
	    this.earlySingletonObjects.remove(beanName);
	    // 已经注册的bean的名称集合
	    this.registeredSingletons.add(beanName);
    }
}

在依赖注入时,会调用getBean->doGetBean->getSingleton,会先从一级缓存取,一级没有去二级取,二级没有去三级取


为什么需要三级缓存

把实例化的bean通过封装成 ObjectFactory 放入第三级缓存做提前暴露,在DefaultSingletonBeanRegistry#getSingleton方法中调用ObjectFactory#getObject,当时放入三级缓存的是匿名内部类

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

调用了AbstractAutowireCapableBeanFactory#getEarlyBeanReference方法,如果这里有用到aop,则会创建代理对象。所以二级缓存保存的是半成品的实例bean或者是代理对象(未进行依赖注入和初始化)。

这么看貌似两层缓存就可以解决循环依赖,为什么设计成三层缓存?

每次从第三级缓存获取(调用ObjectFactory#getObject)都会创建一个代理对象,但需要的是单例对象,这是不行的(比如一个类中有两个相同类型的属性,就会两次获取)。所以借助二级缓存,只需要从二级缓存中拿取,没必要再执行一遍ObjectFactory#getObject方法再产生一个新的代理对象,保证始终只有一个代理对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值