从源码分析spring如何解决循环依赖(一)

spring作为使用最多的java开发框架之一,里面有很多设计模式、编程思想值得学习;今天就从源码层面分析一下spring如何解决循环依赖。

如果想先了解循环依赖bean的创建过程,且对spring创建单例bean的过程比较熟悉的人,可以直接看 三、循环依赖的bean创建过程分析

接下来从源码分析spring如何解决循环依赖。

一、准备工作

  为了比较直观地看到循环依赖的注入效果,我们可以先定义两个互相依赖的Bean

public class Boss {
    private Company company;

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }
}
public class Company {

    private Boss boss;

    public Boss getBoss() {
        return boss;
    }

    public void setBoss(Boss boss) {
        this.boss = boss;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
">
    <bean id="boss" class="cn.wym.springlearn.demo.model.Boss">
        <property name="company" ref="company" />
    </bean>
    <bean id="company" class="cn.wym.springlearn.demo.model.Company">
        <property name="boss" ref="boss"/>
    </bean>

</beans>
public class App {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext  = new ClassPathXmlApplicationContext("application.xml");

        Boss boss = classPathXmlApplicationContext.getBean("boss", Boss.class);

        Company company = classPathXmlApplicationContext.getBean("company", Company.class);

        if (boss.getCompany() == company) {
            System.out.println("boss get the company");
        }

        if (company.getBoss() == boss) {
            System.out.println("company get the boss");
        }

    }

二、单例bean创建源码分析

  在了解spring解决循环依赖前,我们先看看spring如何创建单例bean;这里我们只看一部分代码,单例Bean的创建过程很复杂。

     bean主要在applicationContext refresh阶段完成创建,所以我们进入到AbstractApplicationContext类的refresh方法,所有的applicationContext都是调用的这个refresh方法进行容器刷新。

 在里面我们可以根据注释找到 finishBeanFactoryInitialization(beanFactory)方法,进去

 

 继续往下走,进入到DefaultListableBeanFactory当中

继续往下走,直到来到AbstractBeanFactory的doGetBean方法

 进入getSingleton方法,在这里我们看到了和开头的示意图中的三级缓存

 在第一次调用getSingleton方法的时候,拿到的结果为null,所以我们继续看AbstractBeanFactory的doGetBean方法,如果拿到的对象为空,会进入如下方法(只有singleton的bean才支持循环依赖),并传入createBean方法用于创建bean

进入getSingleton(String beanName,ObjectFactory<?> singletonFactory)方法

在创建前,会把beanName加入到正在创建的集合中,记录bean正在创建

 

factory传入了AbstractBeanFactory的createBean方法,实际调用的是AbstractAutowireCapableBeanFactory里的createBean方法

 在createBean方法里面有个doCreateBean()方法

 在doCreateBean()方法里,单例,且允许循环应用,且正在创建中的bean, 会把早期引用的BeanFactory put到我们示意图中所说的2二级缓存,也就是DefaultSingletonBeanRegistry的singletonFactories中

早期引用的beanfactory也很简单,里面对传入的刚实例化的bean应用了BeanPostProcessor,然后返回bean

继续往下走,此时bean还没有注入属性值,populateBean方法负责为bean注入属性值,其中就包含依赖的其他bean,依赖注入过程就在其中

 

 进入populateBean直接看最后一行代码,应用属性值,进去

 在applyPropertyValues方法里,有如下代码

继续往下走

 

 在resolve reference方法里,再次调用了AbstractBeanFactory的getBean()方法。

三、循环依赖的bean创建过程分析

  在前面的代码中,我们看到,在refresh阶段会调用AbstractBeanFactory的getBean()方法来创建单例bean,所以我们来捋一下循环依赖bean的创建过程,以开头的boss、company为例;依赖注入在populateBean方法中完成,最后也会调用AbstractBeanFactory的getBean方法。

  1. getBean方法获取boss,发现boss不存在,于是进入createBean创建boss,在createBean过程中,把boss的早期引用工厂push到singletonFactories;
  2. 在把boss的早期引用工厂push到singltonfactories后,此时的boss只是一个实例化的bean,没有对属性赋值,于是调用populateBean方法进行属性赋值;
  3. 在属性赋值阶段,发现boss依赖company,于是再次调用getBean方法获取company,此时company也不存在,再次进入createBean创建company
  4. 在createBean创建company阶段,把company的早期引用工厂push到singletonFactories后,再次调用populateBean对company进行属性赋值;因为company依赖boss,会又调用一次getBean方法获取boss,与之前不同的是,此时boss的早期引用工厂存在于三级缓存singletonFactories中,于是从singletonFactories获取boss beanfactory,得到boss,将boss从三级缓存移除,存到二级缓存;把还没有完成属性赋值的boss,赋值给company,然后company创建完成;此时 创建完的company存在于一级缓存singleObjects中,而boss存在于二级缓存earlySingleObjects。
  5. company创建完成后,回到第2步populateBean方法对boss进行赋值的过程中,把company赋值给boss,boss 赋值完成,继续执行后面的代码直至创建完成,最后也保存至一级缓存singletonObjects。

四、debug验证我们所分析的创建过程

   在我们了解了单例bean的创建过程,也推导了循环依赖的单例bean的创建过程后,只需要debug运行程序,验证我们推导的过程即可。对于新手来说,可以把断点打在 AbstractAutowireCapableBeanFactory的doCreateBean方法中的populateBean方法上,在debug过程中,注意观察DefaultSingetonBeanRegistry里,三个缓存当中的数据变化;对spring源码不熟悉的人,不建议一步一步debug,容易晕,因为创建bean是一个递归的过程。

 debug运行,我们看看效果

 一次f9

 再一次f9

 此时原始的boss被赋值给company,boss中的company为空(因为boss还未创建完成),继续f9

 company创建完成,并被赋值给boss,此时boss里面已经有了company;查看DefaultSingletonBeanRegistry里面三个缓存的情况

 

 再一次f9,boss创建完成;可以看到循环依赖bean和我们推导的创建过程一致。

如果比较难理解,多看几次代码,多debug几次,码读百遍,其义自见。

原文地址:https://www.cnblogs.com/RingWu/p/12956579.html