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的实例对象添加到一级缓存当中,共调用者调用
进入方法可以看到实例完成后就添加到了一级缓存当中
图:
文章来源:https://www.toymoban.com/news/detail-819647.html
文章来源地址https://www.toymoban.com/news/detail-819647.html
到了这里,关于SpringBoot的启动流程源码分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!