• 授人以鱼不如授人以渔,首先声明这篇文章并没有过多的总结和结论,主要内容是教大家如何一步一步自己手动debug调试源码,然后总结spring如何解决的循环依赖,最后,操作很简单,有手就行。
  • 本次调试 是使用@Autowired注入,通过来调试源码看spring如何解决的循环依赖问题。
  • 首先创建一个简单的springBoot项目,引入spring-boot-test包即可。可以使用idea提供的spring官网推荐的快速创建。
    • maven依赖

    • <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      </dependency>
    • <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      </dependency>
    • <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.1</version>
      </dependency>
    • <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-test</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.3.1</version>
      <scope>compile</scope>
      </dependency>
    •  编写调试源码的三个类

    • @Getter
      @Setter
      @Component
      public class A {
      @Autowired
      private B b;
      }
    • @Getter
      @Setter
      @Component
      public class B {

      @Autowired
      private A a;
      }
    • @SpringBootTest
      @RunWith(SpringRunner.class)
      public class IocCodeSourceTest {

      @Autowired
      private ApplicationContext applicationContext;

      @Test
      public void test_sourceCode_01() {
      Object bean = applicationContext.getBean("a");
      Object bean1 = applicationContext.getBean("b");
      //通过在此打断点设置可以发现,A和B都初始化完成了
      System.out.println(bean);
      System.out.println(bean1);
      }

      }
    • 注意:这里不能使用lombok提供的注解@Data,因为@Data中重写了equals和hascodes方法,
      System.out.println(bean)

      这一句代码这输出的时候,会去调用bean的hashcode,在计算A对象的hashcode的时候,又调用了属性B的hashcode方法,同理计算B属性的hashcode由去调用了A的,造成了死递归。最终造成线程的虚拟机栈(方法栈)一直入栈,栈溢出。如下图,hashcode9331和9332一直会递归调用。

    •   没时间的童鞋可以直接去看我的github项目地址:https://github.com/coffeebabeCGG/spring/tree/master/src/main/java/com/example/springstudy
  • 调试方法:

首先需要知道,spring默认创建bean都是单例的,而单例的bean是在容器初始化完成后就创建完成。因此这里使用springboot启动调试,最终还是会进入到run方法,到refresh()方法,再去初始化容器。

下面会教几个关键的debug调试点。掌握这几个关键的调试点后,源码怎么走的,可以看最终的调试结果流程图,自己结合源码去一步一步debug。

1、进入getbean()

2、紧接着进去AbstractBeanFactory实现方法

    3、最后进入AbstractBeanFactory的doGetBean()

    4、关键点:在debug上设置条件,因为容器初始化的时候,会有很多spring本身的bean需要先去加载,所以,要在这里设置我们需要去debug观察的bean,我们的bean没有配置beanName,默认使用的类名小写。

    5、接下来就可以一路F9放行,小伙伴们看自己的快捷键,每个人都不一样。当断点停下,就可以观察spring在创建 A B对象的调用栈了。

    关键点:调试源码的时候,深入之后很容易就迷路,有个小技巧。下面截图的左下角红色标注,小伙伴们一定记得学会看方法的调用栈。为了简单理解,可以先这么理解,在开始调用一个方法时候,Jvm会创建一个线程,例如下图就是main线程。

    然后 方法之间的调用是通过栈 这个数据结构来实现的。Jvm中称为java虚拟机栈,每一个方法都是一个栈帧。

    结合下图去理解:

      main线程中(即一个虚拟机栈):run方法是一个栈帧,然后run方法中调用了refreshContxt,那么refreshContext也是一个栈帧,被压入栈顶,以此类推。目前我们当前main线程的虚拟机栈的栈顶是doGetBean方法。

  说了这么多有什么用呢?加入我们在调试过程中,迷路了,我们可以看下调用栈,找到我们来的地方。是哪一个栈帧,可以利用idea提供的下图的回到上一个断点。再回到我们开始的地方,这样一步一步调试就不会迷路了。

  • 调试流程图:

    • 最后附上自己调试之后的总结流程图。下一篇博客,会结合这个流程图,解释一下,三级缓存的具体作用,以及为什么需要使用到了三级缓存?

手把手教你调试SpringBoot启动 IoC容器初始化源码,spring如何解决循环依赖的更多相关文章

  1. SpringBoot框架——从SpringBoot看IoC容器初始化流程之方法分析

    目录 一.概观Spring Boot 二.Spring Boot应用初始化 2.1 初始化入口 2.2 SpringApplication的run方法 2.3 方法分析 三.容器创建与初始化 3.1 ...

  2. 手把手教你撸一套Redux(Redux源码解读)

    Redux 版本:3.7.2 Redux 是 JavaScript 状态容器,提供可预测化的状态管理. 说白了Redux就是一个数据存储工具,所以数据基础模型有get方法,set方法以及数据改变后通知 ...

  3. 手把手教你实现栈以及C#中Stack源码分析

    定义 栈又名堆栈,是一种操作受限的线性表,仅能在表尾进行插入和删除操作. 它的特点是先进后出,就好比我们往桶里面放盘子,放的时候都是从下往上一个一个放(入栈),取的时候只能从上往下一个一个取(出栈), ...

  4. SpringBoot事件监听机制源码分析(上) SpringBoot源码(九)

    SpringBoot中文注释项目Github地址: https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE 本篇接 SpringApplicat ...

  5. 【Spring】Spring IOC原理及源码解析之scope=request、session

    一.容器 1. 容器 抛出一个议点:BeanFactory是IOC容器,而ApplicationContex则是Spring容器. 什么是容器?Collection和Container这两个单词都有存 ...

  6. 手把手教你调试Linux C++ 代码(一步到位包含静态库和动态库调试)

    手把手教你调试Linux C++ 代码 软件调试本身就是一项相对复杂的活动,他不仅要求调试者有着清晰的思路,而且对调试者本身的技能也有很高的要求.Windows下Visual Studio为我们做了很 ...

  7. 【Spring源码解析】—— 结合SpringMVC过程理解IOC容器初始化

    关于IOC容器的初始化,结合之前SpringMVC的demo,对其过程进行一个相对详细的梳理,主要分为几个部分: 一.IOC的初始化过程,结合代码和debug过程重点说明 1. 为什么要debug? ...

  8. 挖坟之Spring.NET IOC容器初始化

    因查找ht项目中一个久未解决spring内部异常,翻了一段时间源码.以此文总结springIOC,容器初始化过程. 语言背景是C#.网上有一些基于java的spring源码分析文档,大而乱,乱而不全, ...

  9. 【spring源码分析】IOC容器初始化(一)

    前言:spring主要就是对bean进行管理,因此IOC容器的初始化过程非常重要,搞清楚其原理不管在实际生产或面试过程中都十分的有用.在[spring源码分析]准备工作中已经搭建好spring的环境, ...

随机推荐

  1. Nmap 简单功能介绍/TCP Header/常见端口

    Nmap:Network Mapper,网络扫描和嗅探的工具包 基本功能有3个: 1.扫描主机端口,嗅探所提供的网络服务 2.探测一组主机是否在线 3.推断主机所用的操作系统,到达主机经过的路由,系统 ...

  2. Windows内核基础知识-1-段寄存器

    Windows内核基础知识-1-段寄存器 学过汇编的应该都知道段寄存器,在Windows里段寄存器有很多,之前可能只接触了ds数据段,cs 代码段这种,今天这个博客就介绍Windows一些比较常用的段 ...

  3. VMware上安装的Ubuntu不显示全屏解决方法

    花费了好一会才把电脑上的Vmware装上,把Ubuntu装到虚拟机上,谁知道Ubuntu不显示全屏,我调了分辨率,奈何Ubuntu里面固定的分辨率没有跟我电脑匹配的,然后开始寻找解决方法,在网上找了很 ...

  4. .net core2.1 迁移.net core 3.1

    1.解决方案->属性-->目标框架 .net core3.1 2.删除旧的Nuget包添加新的NuGet包 3.修改Startup.cs 修改ConfigureServices 修改Con ...

  5. qt 定义插件

    定义的接口----------------------------------------------#ifndef REGEXPINTERFACE_H #define REGEXPINTERFACE ...

  6. C语言之----面向对象的方法实现链表的操作

    1 /* 2 * 详细运行过程: 本程序实现的是对链表的简单的操作,即链表的增 删 改 查 销毁 初始化 3 * 运用面向对象的思想,实现一个类op,op中包括了所有的链表操作方法 4 * 其他的程序 ...

  7. C# 文件的读取与另存为(WPF)

    刚学习时,随便记录的一个小程序.因为有不少人看(应该都是学生),稍作修改,方便阅读. xaml:样式 <!--绑定事件--> <Window.CommandBindings> ...

  8. Lyndon 相关的炫酷字符串科技

    浅谈从 Lyndon Words 到 Three Squares Lemma By zghtyarecrenj 本文包括:Lyndon Words & Significant Suffixes ...

  9. 设置 Qt GUI程序 printf输出到独立控制台

  10. JavaScript 特殊字符

    代码输出\'单引号\"双引号\&和号\\反斜杠\n换行符\r回车符\t制表符\b退格符\f换页符