一步步Debug源码,带你了解Spring如何解决依赖循环

文章目录

  • 1、流程步骤概述
  • 2、代码流程详解。
    • 2.0、测试案例
    • 2.1、第一轮循环getBean代码流程讲解
    • 2.2、第二轮循环getBean代码流程讲解
    • 2.3、第三轮循环getBean代码流程讲解
    • 2.3、第四轮循环getBean代码流程讲解

1、流程步骤概述

(说出来,我就不信面试敢说你没看过源码)

基本的核心流程: getBean->doGetBean->getSingleton->createBean->doCreateBean->createBeanInstance->addSingletonFactory->populateBean->applyPropertyValues->resolveValueNecessary->resolveReference

具体的流程请看下面的流程讲解。

2、代码流程详解。

2.0、测试案例


public class A {
     

    @Autowired
    private B b;

    public B getB() {
     
        return b;
    }

    public void setB(B b) {
     
        this.b = b;
    }
}

----------------------------------------------------------------------------------------------------------------

public class B {
     

    @Autowired
    private A a;

    public A getA() {
     
        return a;
    }

    public void setA(A a) {
     
        this.a = a;
    }
}

----------------------------------------------------------------------------------------------------------------


<?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="a" class="com.ytao.domain.A">
        <property name="b" ref="b"></property>
    </bean>

    <bean id="b" class="com.ytao.domain.B">
        <property name="a" ref="a"></property>
    </bean>

</beans>

----------------------------------------------------------------------------------------------------------------


public static void main(String[] args) {
     

        ApplicationContext config = new ClassPathXmlApplicationContext("spring.xml");
        B b = (B) config.getBean("b");
        System.out.println("b对象为"+b.getA());
        A a = (A) config.getBean("a");
        System.out.println("a对象为"+a.getB());
    }

----------------------------------------------------------------------------------------------------------------

输出结果:
A对象为com.ytao.domain.A@1c9b0314
22:26:16.439 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'a'
B对象为com.ytao.domain.B@45385f75

2.1、第一轮循环getBean代码流程讲解

在讲的Spring的时候,此时我们必会看一个方法那就是refresh。它里面包含了Bean初始化的操作。在类
AbstractApplicationContext中的finishBeanFactoryInitialization 包含了spring容器中bean的创建流程。

一步步Debug源码,带你了解Spring如何解决依赖循环_第1张图片

此时我们进入了finishBeanFactoryInitialization,经过前面代码一些我们不关注的代码,我们直接走到相关创建bean逻辑代码 beanFactory.preInstantiateSingletons();,从在这里看才开始正儿八经的实例化
一步步Debug源码,带你了解Spring如何解决依赖循环_第2张图片

进入到了preInstantiateSingletons,同理我们直接关注创建bean的代码逻辑来到了this.getBean(beanName) ->doGetBean 。 注意这里它先遍历的是对象A

一步步Debug源码,带你了解Spring如何解决依赖循环_第3张图片
然后debug进入getBean -> doGetBean,在这里我们看到了有根据className获取实例的代码Object sharedInstance = getSingleton(beanName);

一步步Debug源码,带你了解Spring如何解决依赖循环_第4张图片

一步步Debug源码,带你了解Spring如何解决依赖循环_第5张图片
根据getSingleton进入来到了他的具体实现方法。 singletonObjects 其实就是实例化完毕后的spring 容器。在这里我们知道其实a对象还没有实例完成,此时我们直接走到return 返回一个null
一步步Debug源码,带你了解Spring如何解决依赖循环_第6张图片

因为此时在容器里我们获取不到我们就需要去创建Bean对象。此时我们来到了createBean方法。注意他是一个lamba表达式
一步步Debug源码,带你了解Spring如何解决依赖循环_第7张图片
根据createBean我们来到了doCreatebean方法 在经过了 createBeanInstance(beanName, mbd, args) 处理后,此时bean实际上已经是一个对象对象,我们先给他堆内存开辟一个空间。至于指向暂定未定。
一步步Debug源码,带你了解Spring如何解决依赖循环_第8张图片

当创建完成后,此时按照流程就应该进入一个类似add的方法。此时我们来到addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))方法,点击方法并且进入。

逻辑如下:这里注意,这里放入的value并不是一个对象。这里方法我们放入的是我们之前看的lmaba表达式,其实也就是提前暴光的单例对象的Cache。

判断三级缓存有没有值,如果没把值放入三级缓存中。然后把二级缓存给清除掉(这里其实二级缓存就没有值)
一步步Debug源码,带你了解Spring如何解决依赖循环_第9张图片

这里注意下,当我们放进入的时候,此时A对象的结构为为如下的结果
一步步Debug源码,带你了解Spring如何解决依赖循环_第10张图片
走完之后,我们该进行填充属性啦。
一步步Debug源码,带你了解Spring如何解决依赖循环_第11张图片
进入到这个方法,我们直接看核心逻辑代码,这里我们来到了applyPropertyValues方法
一步步Debug源码,带你了解Spring如何解决依赖循环_第12张图片
此时到了我们知道此时到了进行填充的步骤啦,此时debug可以看到填充的并不是一个对象,而是RuntimeBeanRefence类型的参数b一步步Debug源码,带你了解Spring如何解决依赖循环_第13张图片

此时我们需要处理我们要填充的值(实际上没成功,因为有依赖循环,继续往下看)
一步步Debug源码,带你了解Spring如何解决依赖循环_第14张图片
然后我们填充的值属于RuntimeBeanRefence类型的,我们进入这个方法。
一步步Debug源码,带你了解Spring如何解决依赖循环_第15张图片
来到这个方法,我们第二次看到getBean这个方法。
一步步Debug源码,带你了解Spring如何解决依赖循环_第16张图片

特点: 第一轮getBean的循环主要是创建A对象的RuntimeBeanRefence,且A且加入了三级缓存singletonFactories,但是填充B的时候发现没有值。开始了第二轮循环

2.2、第二轮循环getBean代码流程讲解

这里特别的我这里会特别指出。重复的地方我就不在说明啦。这里需要强调是第二轮的参数是B (因为A找B找不到,然后B当参数去当做参数,去调用getBean)

重复的流程大致简述为:到了 Object sharedInstance = getSingleton(beanName); 发现三级缓存还是没有,
一步步Debug源码,带你了解Spring如何解决依赖循环_第17张图片
然后继续到创建对象到createBean->doCreateBean,注意这里的传参是第一轮对象A的参数–>b , 此时到了doCreateBean是为把A对象依赖的对象b创建出来。
一步步Debug源码,带你了解Spring如何解决依赖循环_第18张图片
然后老样子我们把创建好的对象,进入addSingletonFactory方法。把参数b放入singletonFactories当中。与此同时到了参数b的时候,此时老样子他也要经过applyPropertyValues方法,又要填充A。 然后根据上面的流程,你会发现你又到getBean方法。 开始套娃啦!

特点: 依赖b的时候,发现b又需要a,此时创建A的依赖参数b的RuntimeBeanRefence的引用,此时A,B都有了RuntimeBeanRefence的引用,且他们都加入了三级缓存singletonFactories

2.3、第三轮循环getBean代码流程讲解

之前讲过,第一轮A作为参数,依赖B的时候,找不到B,第二轮。B当参数的时候,找到不A

此时第三轮,就是A还是当作参数。

此时,还是进入根据上面的循环我们还是进入了到了getSingleton,但是从一级缓存获取为null,且对象属于正在创建的过程中(经过第一轮的循环,此时对象A已经在创建过程中),我们有史以来不同的地方来啦,我们第一次走进去了if

此时我们分别把三级的缓存的值,放入到二级缓存。注意他放入的是ObjectFactory。也就是之前说的lamdba表达式
一步步Debug源码,带你了解Spring如何解决依赖循环_第19张图片

这里注意上面图片的singletonObject = singletonFactory.getObject(); 我们debug进入可以看到它的实现如下。此时它经过了这个方法,并没有走任何方法直接返回的。

一步步Debug源码,带你了解Spring如何解决依赖循环_第20张图片
特点:对象A,有史以来第一次加入了二级缓存

2.3、第四轮循环getBean代码流程讲解

根据上面的结果,我们可以知道。 此时经过上一轮的处理,此时对象B已经有了对象,在经过下次循环时会直接获取到对象b然后进行set 属性即可。此时对象就已经是成品的状态。

(虽然还没有加入一级缓存,但实际上如果只考虑Bean的调用的话,其实二级缓存就够啦,三级缓存是为了解决aop代理对象的问题,后续会更新)

一步步Debug源码,带你了解Spring如何解决依赖循环_第21张图片
此时创建完后成,会进入addsinlgTon方法。

一步步Debug源码,带你了解Spring如何解决依赖循环_第22张图片
在这里,把singletonObject放入了到了一级缓存中
一步步Debug源码,带你了解Spring如何解决依赖循环_第23张图片
都有值啦,… 剩下的应该就没啥讲的啦把。

你可能感兴趣的:(java)