准备工作

分析源码的好处

分析源码可以培养我们代码的架构思维、深入理解框架

分析源码的原则

  • 定焦原则:抓主线
  • 宏观原则:站在上帝视角,关注源码结构和业务流程,淡化具体某行代码的编写细节

    方法与技巧

  • 断点,观察调用栈

  • 反调,通过idea的find usages
  • 经验

    源码构建流程

  1. 安装gradle
  2. 下载源码,github:https://github.com/spring-projects/spring-framework
  3. 导入:需要耗费很长的时间,本人花费大约22分钟
  4. 编译工程:编译顺序为core-oxm-context-beans-aspects-aop,编译的时候选择gradle->工程->tasks->other->compileTestJava

问题说明

本人使用的是5.2.2版本,遇到了两个问题:

  1. 遇到 Test events were not received 问题,解决方案参考:https://stackoverflow.com/questions/57795263/test-events-were-not-received-when-run-tests-using-intellij ,在idea中配置:File -> Settings ->Build,Execution, Deployment -> Build Tools -> Gradle -> run tests using中选择 使用idea,而不是使用gradle
  2. 遇到 NoClassDefFoundError: org/hamcrest/SelfDescribing 问题,这个是junit版本问题,我引入的是``testCompile group: 'junit', name: 'junit', version: '4.12',修改为如下
    1. dependencies {
    2. compile(project(":spring-context"))
    3. compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.8.6'
    4. // testCompile group: 'junit', name: 'junit', version: '4.12'
    5. testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
    6. testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
    7. }

    IOC容器初始化的主体流程

容器体系

先说一下容器,spring ioc容器并不是map,map只是容器的一个成员而已,被称作单例池子,也就是scope属性为singleton的bean对象才会放在map中被容器管理,容器是一组组件和过程的集合,包括了前面说的生命周期,例如BeanFactory、单例池、BeanPostProcessor等以及之间的协作。

这里主要说明IOC容器的继承体系,如图,这里展示了部分,是以ApplicationContext接口,因为我们最常用的就是这个了:
applicationContext继承体系.png
最顶层的容器接口是 BeanFactory,它定义了容器的最基本的行为,并不是全部,这也是Spring设计的优雅的地方,将不同的功能封装在不同的接口:

  • BeanFactory:顶层接口,提供了最基础的行为
    • getBean:重载的getBean方法用来获取容器中的对象
    • getBeanProvider:重载方法,放回指定Bean的提供程序,也就是获取是谁创建了这个Bean
    • containsBean:判断当前容器是否存在bean
    • isSingleton:判断Bean是否为单例Bean
    • isPrototype:判断Bean是否为多例Bean
    • isTypeMatch:判断获取的Bean是否为指定类型
    • getType:获取Bean的类型
    • getAliases:获取别名
    • FACTORY_BEAN_PREFIX静态属性:用来获取 FactoryBean 对象,因为我们在使用 FactoryBean对象创建Bean的时候直接会获取创建的Bean对象,而不是 BeanFactory对象
  • HierarchicalBeanFactory接口:提供用来获取父BeanFactory的接口,获取父工厂
  • ListableBeanFactory接口:提供的集合相关的Bean接口,需要的方法全部都是集合相关,比如getBeanNamesForType用来获取某个属性的全部Bean
  • ResourceLoader接口:不属于容器接口,是加载资源的接口
  • EnvironmentCapable接口:国际化接口
  • MessageSource接口:消息定义接口
  • ApplicationEventPublisher接口:监听接口

处理器以及加载机制

加载机制

Bean的构造器方法

构造器执行被 AbstractApplicationContext#refresh#finishBeanFactoryInitialization(beanFactory)

Bean的初始化方法,也就是InitializingBean

初始化方法被 AbstractApplicationContext#refresh#finishBeanFactoryInitialization(beanFactory)

BeanFactoryPostProcessor处理器

初始化方法,也就是构造器 AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors(beanFactory)
方法的执行 AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors(beanFactory)

BeanPostProcessor处理器

初始化方法 AbstractApplicationContext#refresh#registerBeanPostProcessors(beanFactory)
前置方法的执行 AbstractApplicationContext#refresh#finishBeanFactoryInitialization(beanFactory)
后置方法的执行 AbstractApplicationContext#refresh#finishBeanFactoryInitialization(beanFactory)

上面是Bean的生命周期涉及的一些执行流程和处理器,可以看到斗鱼AbstractApplicationContext的refresh方法相关,下面看看这个方法干了什么?

refresh方法的理解

完成ioc容器初始化的一个核心方法,该方法可被在执行过程中调用来刷新容器。

  1. @Override
  2. public void refresh() throws BeansException, IllegalStateException {
  3. // 对象锁,防止在初始化的时候被关闭或者是关闭的时候刷新容器
  4. synchronized (this.startupShutdownMonitor) {
  5. // 容器初始化的准备工作
  6. // Prepare this context for refreshing.
  7. prepareRefresh();
  8. // 初始化Bean工厂,默认实现是DefaultListableBeanFactory
  9. // 并且完成加载 BeanDefinition, 加载调用的方法为loadBeanDefinitions(beanFactory); 并且注册到 BeanDefinitionRegistry
  10. // Tell the subclass to refresh the internal bean factory.
  11. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  12. // BeanFactory的初始化配置
  13. // Prepare the bean factory for use in this context.
  14. prepareBeanFactory(beanFactory);
  15. try {
  16. // 后置处理
  17. // postProcessBeanFactory为扩展口
  18. // Allows post-processing of the bean factory in context subclasses.
  19. postProcessBeanFactory(beanFactory);
  20. // 实例化BeanFactory的后置处理器,并调用后置处理器的方法
  21. // Invoke factory processors registered as beans in the context.
  22. invokeBeanFactoryPostProcessors(beanFactory);
  23. // 注册BeanPostProcessor后置处理器,在创建Bean的前后执行
  24. // Register bean processors that intercept bean creation.
  25. registerBeanPostProcessors(beanFactory);
  26. // 初始化国际化等信息
  27. // Initialize message source for this context.
  28. initMessageSource();
  29. // 初始化事件分发器
  30. // Initialize event multicaster for this context.
  31. initApplicationEventMulticaster();
  32. // 自定义刷新逻辑
  33. // Initialize other special beans in specific context subclasses.
  34. onRefresh();
  35. // 注册监听器,实现了 ApplicationListener 的监听器
  36. // Check for listener beans and register them.
  37. registerListeners();
  38. // 调用方法,初始化并创建单例Bean,并且设置属性
  39. // 以及调用各种初始化方法:比如说 InitializingBean的初始化,init-method等
  40. // 调用后置处理器的方法,对实例Bean进行前置和后置处理
  41. // Instantiate all remaining (non-lazy-init) singletons.
  42. finishBeanFactoryInitialization(beanFactory);
  43. // 完成刷新
  44. // Last step: publish corresponding event.
  45. finishRefresh();
  46. }
  47. catch (BeansException ex) {
  48. if (logger.isWarnEnabled()) {
  49. logger.warn("Exception encountered during context initialization - " +
  50. "cancelling refresh attempt: " + ex);
  51. }
  52. // Destroy already created singletons to avoid dangling resources.
  53. destroyBeans();
  54. // Reset 'active' flag.
  55. cancelRefresh(ex);
  56. // Propagate exception to caller.
  57. throw ex;
  58. }
  59. finally {
  60. // Reset common introspection caches in Spring's core, since we
  61. // might not ever need metadata for singleton beans anymore...
  62. resetCommonCaches();
  63. }
  64. }
  65. }

整个大致的流程为:
初始化BeanFactory -> 初始化BeanFactory的处理器,并执行处理器方法 -> 注册Bean的后置处理器 -> 实例化Bean信息、完成初始化信息、执行Bean的后置处理器

BeanFactory的创建流程

从refresh开始,在 obtainFreshBeanFactory 该方法中我们完成了工厂的创建以及BeanDefinition的创建,这里说下流程
时序.png
整个流程如图所示,里面代码相对简单,也就是判断当前是否有工厂,有就销毁重新创建,没有则创建。

BeanDefinition的加载解析以及注册

这个流程是层层调用,下面来一个文字流程

  • AbstractApplicationContext#refresh开始,还是创建工厂的方法 obtainFreshBeanFactory
  • AbstractApplicationContext#obtainFreshBeanFactory中调用refreshBeanFactory()方法
  • AbstractRefreshableApplicationContext#refreshBeanFactory调用loadBeanDefinitions
  • AbstractXmlApplicationContext#loadBeanDefinitions方法
  • AbstractXmlApplicationContext#loadBeanDefinitions,重载方法的调用
  • AbstractBeanDefinitionReader#loadBeanDefinitions
  • AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String)
  • AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set)
  • AbstractBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource…)
  • XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)
  • XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)
  • XmlBeanDefinitionReader#doLoadBeanDefinitions
  • XmlBeanDefinitionReader#registerBeanDefinitions
  • DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
  • DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
  • DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
  • DefaultBeanDefinitionDocumentReader#parseDefaultElement
  • DefaultBeanDefinitionDocumentReader#processBeanDefinition
    • 解析xml中的Bean元素,封装为BeanDefinition
      • BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element)
      • BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)
    • 注册BeanDefinition
      • BeanDefinitionReaderUtils#registerBeanDefinition
      • DefaultListableBeanFactory#registerBeanDefinition

整个调用链就是这样的,整个流程就是将xml解析为Resource资源,将Resource资源转化为Document对象,然后解析document对象,针对每一个标签进行处理,这里再5.2版本是针对四个标签进行了处理,分别是import、bean、beans、alias,之后对于document中的元素进行解析,封装成为BeanDefinition对象,最后将BeanDefinition放入beanDefinitionMap中。
这就是整个解析和注册的流程,注册其实就是放入map中。

Bean对象的创建流程

继续是在AbstractApplicationContext中的refresh方法来,前面说过具体的解析所调用的方法是 finishBeanFactoryInitialization

  • AbstractApplicationContext#finishBeanFactoryInitialization
  • DefaultListableBeanFactory#preInstantiateSingletons : 只加载单例Bean
  • AbstractBeanFactory#getBean(java.lang.String)
  • AbstractBeanFactory#doGetBean
  • DefaultSingletonBeanRegistry#getSingleton
  • AbstractAutowireCapableBeanFactory#createBean
  • AbstractAutowireCapableBeanFactory#doCreateBean
  • AbstractAutowireCapableBeanFactory#initializeBean

initializeBean这个是最重要的初始化方法,里面包括初始化方法、后置处理器等的执行。

循环依赖问题

循环依赖问题是对象之间相互依赖的一种现象,比如说对象A依赖对象B,对象B依赖对象A,这种现象就叫做循环依赖。
根据Spring注入的方式有两种:

  • 构造器初入
  • setter注入

对于构造器注入来说,循环依赖是解决不了的,Spring只能解决setter注入方法,并且Bean的scope属性必须是singleton的循环依赖问题。下面说处理方案就知道为什么了。

方案

对于循环依赖,Spring的解决方案是提前暴露Bean,通过三级缓存来实现

  • 一级缓存:singletonObjects,单例池
  • 二级缓存:earlySingletonObjects
  • 三级缓存:singletonFactories

如何做的呢?就以A、B为例,根据我们前面所说的对象生命周期,Bean初始化的第一步就是实例化Bean,接下来会设置属性值,而设置属性值才会有循环依赖的问题,所以我们在实例化Bean之后就放入三级缓存中,之后在设置属性值的时候会发现依赖B,这个时候会加载B的Bean,初始化B设置属性值的时候发现依赖A,这个时候会从缓存中 A的实例,先从一级缓存,再从二级缓存,都没有发现会从三级缓存中拿,注意这个时候A对象是不完全的,仅仅实例化完成,属性还没有设置,之后就将实例化好的A从三级缓存中移除,然后放入二级缓存,B现在有了A的对象,就可以完成初始化了;B的初始化完成,接下来返回继续A的初始化,当A的生命周期完成之后就放入单例池,移除二级缓存中的A对象实例。

那为什么构造器注入无法解决呢?原因是三级缓存的实现建立在提前暴露Bean,构造器注入的话Bean就无法完成实例化,进一步也就没办法解决构造器注入的问题了。

至于为什么scope属性必须是singleton,原因是prototype属性的Bean不在Spring IoC容器的管理范围,只负责创建,不负责管理,所以也没办法解决。