说透 Spring循环依赖和3级缓存

Bean加载的总体流程

FactoryBean的使用

地位:FactoryBean接口是Spring重要的拓展接口

用途:用于复杂的初始化 或者 框架集成

使用:

自定义类继承接口FactoryBean

原理:spring的getBean方法会回调FactoryBean的getObject方法

缓存中获取单例bean

说明下3级缓存指的是什么:

map的名称完整性几级缓存
singletonObjects完整的bean(就是我们最终使用的)第一级缓存
earlySingletonObjects半完整的bean(循环依赖时会出现)第二级缓存
singletonFactories工厂bean(使用FactoryBean接口会出现)第三级缓存

从3级缓存中获取bean,原则是第一级没有去第二级,第二级没有去第三极查找。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从第一级缓存中查找
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 从第二级缓存中查找
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            // 加锁
            synchronized (this.singletonObjects) {
                // Consistent creation of early reference within full singleton lock
                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) {

                            // 调用工厂方法【1、工厂模式下会调用;2、循环依赖下会调用】
                            singletonObject = singletonFactory.getObject();

                            this.earlySingletonObjects.put(beanName, singletonObject);
                            // 从singletonFactories中remove掉这个ObjectFactory
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}

从bean的实例中获取对象

ObjectFactory 与 FactoryBean接口的比较

相同点:都有getObject方法

不同点:作用完全不相同。

ObjectFactoryFactoryBean
相同点有getObject方法有getObject方法
不同点用于“第三级缓存”,即singletonFactories1、用于复杂bean的初始化2、用于框架集成(如mybatis与spring)

不同点体现的代码:

  • ObjectFactory

AbstractAutowireCapableBeanFactory#doCreateBean方法的代码片段(功能:加入第三极缓存)

// 默认下每个bean都会进入这个方法
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

DefaultSingletonBeanRegistry#getSingleton方法代码片段(功能:查找3级缓存)

// 从第三极缓存中查找
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {

    // 调用工厂方法【1、工厂模式下会调用;2、循环依赖下会调用】
    singletonObject = singletonFactory.getObject();

    this.earlySingletonObjects.put(beanName, singletonObject);
    // 从singletonFactories中remove掉这个ObjectFactory
    this.singletonFactories.remove(beanName);
}
  • FactoryBean

FactoryBean起作用的调用链路:

getBean —> AbstractBeanFactory#getObjectForBeanInstance —> FactoryBeanRegistrySupport#getObjectFromFactoryBean —> doGetObjectFromFactoryBean

// 代码片段

private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
    // 执行FactoryBean的getObject方法。
    /**
     * 注意:跟函数式接口ObjectFactory的定义非常相似(在getBean方法中出现,getObject的作用是定义模板方法中的特殊操作)
     * [T getObject() throws BeansException;] 与 [T getObject() throws Exception;]
     */
    Object object = factory.getObject();
    return object;
}

获取单例

描述的DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory<?>)方法

1、检查缓存是否已经加载过

2、如没有加载,则记录beanName正在加载的状态

3、加载单例前记录加载状态

4、通过调用参数传入的ObjectFactory的getObject方法完成实例化bean

5、加载单例后的处理方法调用

6、将结果记录至缓存并删除加载bean过程中的所记录的各种辅助状态。

准备创建Bean

循环依赖

要理解spring是如何解决循环依赖的,我们需要先了解我们吧什么定义循环依赖。最简单的,bean1中有一个属性prop2,bean2中有一个属性prop1,prop2指向bean2,prop1指向bean1,这就是一个”最简单“的循环依赖的例子。

上面的例子太简单了,不能满足我们研究spring中3级缓存如何在循环依赖中的作用,所以,我们必须要把例子复杂起来一些。

测试环境准备

1、首先,我们把测试的代码贴出来

Bean_1.java、Bean_2.java、Bean_3.java文件:

@Data
public class Bean_1 {
    private Bean_2 prop_2;
    private Bean_3 prop_3;
}

@Data
public class Bean_2 {
    private Bean_1 prop_1;
}

@Data
public class Bean_3 {
    private Bean_1 prop_1;
}

application.xml文件:

    <bean id="bean_1"
        class="com.firefish.springsourcecodedeepanalysis.cycle.setter.Bean_1"
        scope="singleton">
        <property name="prop_2" ref="bean_2"></property>
        <property name="prop_3" ref="bean_3"></property>
    </bean>

    <bean id="bean_2"
        class="com.firefish.springsourcecodedeepanalysis.cycle.setter.Bean_2"
        scope="singleton">
        <property name="prop_1" ref="bean_1"></property>
    </bean>

    <bean id="bean_3"
        class="com.firefish.springsourcecodedeepanalysis.cycle.setter.Bean_3"
        scope="singleton">
        <property name="prop_1" ref="bean_1"></property>
    </bean>

Main.java文件:

public class Main {
    public static void main(String[] args) {
        XmlBeanFactory context = new XmlBeanFactory(new ClassPathResource("com/firefish/springsourcecodedeepanalysis/cycle/setter/application.xml"));
        System.out.println(context.getBean("bean_3"));
    }
}

2、然后,我们把bean的关系也贴出来

3、到这里其实我们就具备了研究3级缓存和循环依赖的条件了,只要运行Main.java。

bean生命周期中关于循环依赖的介绍

循环依赖的解决与bean的生命周期息息相关,我们不得不介绍下与循环依赖关联的一些点。

下面是bean生命周期与循环依赖关系密切的流程:

1、从3层级缓存中查找bean(getSingleton方法)

2、doCreateBean 之前记录"正在加载bean"(beforeSingletonCreation方法)

3、实例化bean(createBeanInstance方法调用构造器)

4、加入到第三级缓存(addSingletonFactory方法)

5、属性填充、初始化、后置处理器(重点是属性填充)

6、结束beanA的创建,移除bean的创建状态

画图演示存在循环依赖下,bean在3级缓存中的迁移过程

以上面的代码为实例,在循环依赖下,下面将用图片的形式展示bean_1、bean_2、bean_3在bean生命周期执行过程中如何通过3级缓存解决循环依赖问题。

1、从3层级缓冲中查找bean_3,发现找不到,则创建bean_3;先记录bean_3正在创建中的状态,实例化bean_3,把bean_3加入第三级缓存中

2、对bean_3进行属性填充,发现依赖prop_1,进而去创建bean_1

3、从3层级缓冲中查找bean_1,发现找不到,则创建bean_1;先记录bean_1正在创建中的状态,实例化bean_1,把bean_1加入第三级缓存中

4、对bean_1进行属性填充,发现依赖prop_2、prop_3;依据属性填充的先后顺序,先getBean_2,后getBean_3;进而就去创建bean_2

5、从3层级缓冲中查找bean_2,发现找不到,则创建bean_2;先记录bean_2正在创建中的状态,实例化bean_2,把bean_2加入第三级缓存中

6、对bean_2进行属性填充,发现依赖prop_1;进而去getBean_1

7、发现bean_1已经在创建中了,从3级缓冲的第三级缓存中可以找到bean_1了,调用bean_1的getObject方法(这里完成应用SmartInstantiationAwareBeanPostProcessor接口处理器来增强原始bean的功能。aop就是通过这个实现的)。从这里开始似乎开始循环依赖的结束了…。

8、把可能增强后的bean_1加入到第二级缓存中(为什么???),现在的情况如下图了。

9、目前为止,完成了bean_2的属性填充,然后它的初始化等等流程…,最后完成了bean_2的完整创建,它被移动到第一级缓存供用户使用。

10、接着步骤4,bean_1的属性prop_3还没有完成,现在进行bean3获取,即getBean_3

11、发现bean_3已经在创建中了,从3级缓冲的第三级缓存中可以找到bean_3了,调用了bean_3的getObject方法,状态如下了。

12、到这里,完成了bean_1的prop_2、prop_3的属性填充,以为着bean_1也就完整了创建,状态如下:

13、最后完整了bean_3的创建

过程的状态图都演示完了,下面总结下各种map的作用吧。(多debug几遍源码可能更好理解)

几种map的作用
  • singletonsCurrentlyInCreation(记录了那些bean正在创建中)

  • singletonObjects(完整的bean的缓存)

  • earlySingletonObjects

为什么需要earlySingletonObjects的缓存???

1、如果没有它,当多次依赖bean(如上文中的bean_1)将不得不从第三级缓存的getObject方法中重新创建新的bean,这就违反了”单例bean“;

2、对于这种中间状态的bean(如上文中的bean_1),总要有一个地方吧它缓存起来

总结:earlySingletonObjects的作用就是防止多次创建bean,也就是作为”半初始化“单例bean的一个中间缓存。

  • singletonFactories

这个只有一个作用:应用 SmartInstantiationAwareBeanPostProcessor 接口完整bean的功能增强,如aop功能

总结

可以重点了解下循环依赖下,3个bean状态在3级缓存中的迁移情况,可以debug2遍看看;

了解后就不难理解这几种map的作用了。下面贴几张总结性的一些图,或许有用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fire Fish

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值