• 从源码分析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几次,码读百遍,其义自见。

  • 相关阅读:
    .NET 分布式架构开发实战之一
    frame中隐藏横向滚动条
    实时检测网络状态及是否可以连接Internet
    jquery表格插件推荐
    FireFox窗体frameset,iframe间的js调用方法
    用C#实现实现简单的 Ping 的功能,用于测试网络是否已经联通
    一个阴历阳历互相转化的类
    CSS技巧 — 不使用图片实现圆角、阴影、渐变等功能
    Windows下命令行下启动ORACLE服务
    使用C#进行点对点通讯和文件传输(通讯基类部分)
  • 原文地址:https://www.cnblogs.com/RingWu/p/12956579.html
Copyright © 2020-2023  润新知