循环依赖

Spring 通过提前曝光机制,利用三级缓存解决循环依赖。

在 Spring 创建 Bean 的核心代码 doGetBean 中,在实例化 bean 之前,会先尝试从三级缓存获取 bean,这也是 Spring 解决循环依赖的开始。

| 缓存中获取 bean

// AbstractBeanFactory.java
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

        final String beanName = transformedBeanName(name);
        Object bean;

        // 2. 尝试从缓存中获取bean
        Object sharedInstance = getSingleton(beanName);
        ...
}

 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 从一级缓存获取,key=beanName value=bean
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                // 从二级缓存获取,key=beanName value=bean
                singletonObject = this.earlySingletonObjects.get(beanName);
                // 是否允许循环引用
                if (singletonObject == null && allowEarlyReference) {
                    /**
                     * 三级缓存获取,key=beanName value=objectFactory,objectFactory中存储getObject()方法用于获取提前曝光的实例
                     *
                     * 而为什么不直接将实例缓存到二级缓存,而要多此一举将实例先封装到objectFactory中?
                     * 主要关键点在getObject()方法并非直接返回实例,而是对实例又使用
                     * SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法对bean进行处理
                     *
                     * 也就是说,当spring中存在该后置处理器,所有的单例bean在实例化后都会被进行提前曝光到三级缓存中,
                     * 但是并不是所有的bean都存在循环依赖,也就是三级缓存到二级缓存的步骤不一定都会被执行,有可能曝光后直接创建完成,没被提前引用过,
                     * 就直接被加入到一级缓存中。因此可以确保只有提前曝光且被引用的bean才会进行该后置处理
                      */
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        /**
                         * 通过getObject()方法获取bean,通过此方法获取到的实例不单单是提前曝光出来的实例,
                         * 它还经过了SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法处理过。
                         * 这也正是三级缓存存在的意义,可以通过重写该后置处理器对提前曝光的实例,在被提前引用时进行一些操作
                          */
                        singletonObject = singletonFactory.getObject();
                        // 将三级缓存生产的bean放入二级缓存中
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        // 删除三级缓存
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }



三级缓存分别是:

singletonObject:一级缓存,该缓存 key = beanName, value = bean;这里的 bean 是已经创建完成的,该 bean 经历过实例化->属性填充->初始化以及各类的后置处理。因此,一旦需要获取 bean 时,我们第一时间就会寻找一级缓存。 

earlySingletonObjects:二级缓存,该缓存 key = beanName, value = bean;这里跟一级缓存的区别在于,该缓存所获取到的 bean 是提前曝光出来的,是还没创建完成的。

也就是说获取到的 bean 只能确保已经进行了实例化,但是属性填充跟初始化肯定还没有做完,因此该 bean 还没创建完成,仅仅能作为指针提前曝光,被其他 bean 所引用。

singletonFactories:三级缓存,该缓存 key = beanName, value = beanFactory;在 bean 实例化完之后,属性填充以及初始化之前,如果允许提前曝光,spring 会将实例化后的 bean 提前曝光,也就是把该 bean 转换成 beanFactory 并加入到三级缓存。

在需要引用提前曝光对象时再通过 singletonFactory.getObject() 获取。

 

 三级缓存的添加

我们回到添加三级缓存,添加 SingletonFactory 的地方,看看 getObject() 到底做了什么操作?

    this.addSingletonFactory(beanName, () -> {
        return this.getEarlyBeanReference(beanName, mbd, bean);
    });

 

可以看到在返回 getObject() 时,多做了一步 getEarlyBeanReference 操作,这步操作是 BeanPostProcess 的一种,也就是给子类重写的一个后处理器,目的是用于被提前引用时进行拓展。



posted @   KLAPT  阅读(44)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
历史上的今天:
2021-07-20 127.0.0.1
2021-07-20 MySQL 中的共享表空间与独立表空间
点击右上角即可分享
微信分享提示