SpringBoot的启动流程源码分析

new 一个IOC容器,传入配置好的文件xml,在这个地方打bug

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);

SpringBoot的启动流程源码分析_第1张图片

在这个debug的栈帧中,下面几个不用看,直接看到getBean

内容如图所示,name传的就是我们在xml的bean标签的id,这里是instanceA

SpringBoot的启动流程源码分析_第2张图片

进入到doGetBean后,因为我是从IOC初始化容器debug进来的所以第一次通过。

Object sharedInstance = getSingleton(beanName); //sharedInstance为null

从一级缓存获取到的值是为null,所以需要进行创建。

SpringBoot的启动流程源码分析_第3张图片

上面下来后,这里判断当前bean是否产生了循环依赖,注释意思是“如果我们已经创建失败这个bean实例:我们大概在一个循环引用”,意思就是如果产生循环依赖这里就是true,则直接抛异常。但是并没有,所以往下走。

SpringBoot的启动流程源码分析_第4张图片

这里判断是否为父子容器(父子容器能够解决在相同bean里名字重复依赖注入是导致的报错,因为父子容器之间是隔离的,但是子容器可以去访问父容器,父容器不可以访问子容器,而子容器可以通过任何方式注入父容器的bean,调用子容器的getBean方法获取bean的时候,会沿着当前容器开始向上面的容器进行查找,直到找到对应的bean为止),显然只是用了一个单例进行所以返回null,继续往下

进入方法

SpringBoot的启动流程源码分析_第5张图片

这行代码检查当前bean是否为抽象的,显然这个bean不是

当我们在xml中声明时,它才会是true,是true就会抛出异常,因为抽象类不能被实例化

这里的dependsOn表明当前bean是否与其他bean具有依赖关系

SpringBoot的启动流程源码分析_第6张图片

这也是在xml声明,显然这里没有,继续往下

SpringBoot的启动流程源码分析_第7张图片

这里传入一个lambda表达式进去,其中createBean就是创建bean的逻辑方法,而且这里也是返回instanceA实例的地方,所以就需要重点观察这里

SpringBoot的启动流程源码分析_第8张图片

进入方法后我们就可以发现,它去获取了concurrentHashmap里面的值,那肯定是为null的,注意这里返回的是Object类型的,所以我们只需排查出返回Object类型的方法就可以

先看这行代码,它会把当前beanId装入set集合中,如果此时又有一个线程来创建相同的beanId就会抛异常,进入这个方法

SpringBoot的启动流程源码分析_第9张图片

我们可以看到如果if条件成立就会抛异常

定位到这里,其实就是调用了前面传进来的lambda表达式的那个返回值,也就createBean()方法

SpringBoot的启动流程源码分析_第10张图片

进入createBean()方法

SpringBoot的启动流程源码分析_第11张图片

它返回是一个Object类型的对象,那就定位到了具有返回object对象的方法上,spring源码中一般以do开头的方法就是真正干活的

SpringBoot的启动流程源码分析_第12张图片

此时定位到这两个画线的方法,其返回类型都是Object

SpringBoot的启动流程源码分析_第13张图片

通过debug发现下面一个返回了bean的实例对象,这就是我们需要了解的方法,进入这个方法

SpringBoot的启动流程源码分析_第14张图片

debug在该方法继续向下

SpringBoot的启动流程源码分析_第15张图片

划线就是通过反射创建了bean对象,但是是一个空壳子,就是没有属性,里面很实现很复杂,debug继续往下

SpringBoot的启动流程源码分析_第16张图片

此时会进入addSingletonFactory()方法

SpringBoot的启动流程源码分析_第17张图片

这个方法的作用就是把我们更改获取得到的bean的早期对象(空壳对象)放入三级缓存当中(一级缓存存放的就是实例对象,二级缓存充当一个一个中介,存放的也是早期对象),三级缓存用于解决循环依赖问题

添加完成后继续往下

populateBean(beanName, mbd, instanceWrapper);//用于暴露上一步早期(空壳)对象

如果A依赖B,B也依赖于A,就是A中注入了B则先会去创建B的实例对象进行注入,而此时B走的流程跟A是一模一样的,等到B也执行到populateBean(beanName, mbd, instanceWrapper)时,发现需要注入A对象,此时,就又会从刚开始进行的流程走下来,但这时候就不一样了,B需要注入A时,因为前面A已经将早期对象放入了三级缓存当中,所以执行到

SpringBoot的启动流程源码分析_第18张图片

这里时getSingleton就不为null了,

SpringBoot的启动流程源码分析_第19张图片

进入getSingleton()方法就可以发现,它具体是先去一级缓存拿发现没有,然后再去二级缓存拿还是没有,有去三级缓存拿,找到了A的早期对象,此时就把三级缓存中的A的早期对象放入到二级缓存当中,最后就会直接返回A的早期对象给B,此时B拿到A的早期对象后会继续的往下执行,最后B得到最终实例化,注入进A当中,然后A也完成了实例化,这里就是使用了多级缓存解决了循环依赖的问题

补充:

SpringBoot的启动流程源码分析_第20张图片

画线方法能够保证如果A,B都为代理对象,此时能获取他们代理对象的引用,是一个后置的处理器,

SpringBoot的启动流程源码分析_第21张图片

在把A的早期对象加入三级缓存前,传了一个表达式,在B获取A的早期对象时,就会执行这个方法

跟踪就去,就是这个方法实现的

this.ingletonFactories.put(beanName, singletonFactory);//放入三级缓存 this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName);

就是这个方法

回到getSingleton()方法,调用lambda表达式的createBean()后返回了InstanceA的实例对象

往下的这行代码就是删除前面加入到Set集合里的BeanId

如果删除失败抛出异常

执行到这里就把instanceA的实例对象添加到一级缓存当中,共调用者调用

SpringBoot的启动流程源码分析_第22张图片

进入方法可以看到实例完成后就添加到了一级缓存当中

图:

SpringBoot的启动流程源码分析_第23张图片

SpringBoot的启动流程源码分析_第24张图片

你可能感兴趣的:(spring,boot,java,spring)