SpringBoot的启动流程源码分析

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

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);

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

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

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

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

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

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

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

进入方法

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

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

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

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

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

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

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

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

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

进入createBean()方法

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

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

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

debug在该方法继续向下

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

此时会进入addSingletonFactory()方法

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

添加完成后继续往下

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

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

这里时getSingleton就不为null了,

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

补充:

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

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

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

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

就是这个方法

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

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

如果删除失败抛出异常

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

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

图:

  • 23
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱生活,更爱技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值