
Spring IoC 容器初始化
作者 |jaxer
来源 | WriteOnRead(ID:WriteOnRead)
概述
上篇文章「Spring 中的 IoC 容器」从整体介绍了 Spring IoC 容器的相关概念和大致实现流程,本文要进入源码来一探究竟了。
这里仍以前文的代码为例进行分析,测试代码如下:
PS: 此处 Spring Framework 版本为 5.2.12.RELEASE,其他版本可能略有不同。
代码分析
从 ClassPathXmlApplicationContext 的构造器进入它的代码。
ClassPathXmlApplicationContext 有很多重载的构造器,不过多数都会调用到下面这个:
该构造器主要做了三件事:
- 调用父类的构造器,保存传入的父容器
- 保存配置信息,在本例中就是 application-ioc.xml
- 刷新 IoC 容器
其中最核心的就是第三步,也是最复杂的。
由于 ClassPathXmlApplicationContext 的整体继承结构比较复杂,为了便于分析其核心实现,这里先暂时忽略它实现的接口,只看它的类继承结构:
前面两个步骤的代码不再深入分析,这里直接进入第三步,也就是 refresh 方法(该方法是在 AbstractApplicationContext 类中实现的):
refresh 方法里面封装了很多方法,每个方法里面又是一大堆代码……
刚开始看到这里可能会被吓到(我当初就是这样被劝退的)。
其实不必,代码虽然很多,但读起来还是有迹可循的:就是要先找到一条主线。就像一棵树,先找到树干,其它的都是细枝末节。先主后次,要不很容易陷进去、读起来一团乱麻。
PS: 之前写过一篇如何阅读 JDK 源码的文章「我是如何阅读JDK源码的?」,有兴趣可以参考一下。
但二者又有些不同:JDK 源码相对独立,一般关联性不大,而 Spring 的代码前后关联太多。
这里的主线是什么呢?
就是 obtainFreshBeanFactory 方法(它的实现在 AbstractRefreshableApplicationContext 类中),如下:
refreshBeanFactory 看名字可以推测是刷新 IoC 容器,它主要做了三件事:
- 如果 IoC 容器已经存在,则销毁容器中的对象、关闭容器(正如其名,refresh,是一个全新的,就要先把旧的干掉)。
- 创建 BeanFactory,即 DefaultListableBeanFactory,它就是 Spring IoC 容器的默认实现。
- 加载 BeanDefinition,也就是从配置文件(application-ioc.xml)中加载我们定义的 bean 信息,这一步也是最复杂的。
这里的 loadBeanDefinitions 是一个抽象方法?!
是的。这就是设计模式中的「模板模式(Template Pattern)」,这个模式不难理解,不了解也不影响,有空可以再看。
JDK 中的 AbstractQueuedSynchronizer(AQS) 也使用了模板模式,前文「JDK源码分析-AbstractQueuedSynchronizer(2)」可以参考。
loadBeanDefinitions 方法其实就是把实现代码放在子类中了。
What?它的子类有那么多,哪个才是真·儿子呢?
还记得上面那个类的继承结构图吗?
所以,这里它的子类实现指的就是 AbstractXmlApplicationContext#loadBeanDefinitions 方法:
它来了,XmlBeanDefinitionReader,是用来读取 BeanDefinition 的。
这里 BeanDefinition 的类型有两种:Resource 和 String。其实本质上还是一种,即 Resource,String 最终还是要转为 Resource。
这两个 loadBeanDefinitions 最终都会调用 XmlBeanDefinitionReader#loadBeanDefinitions 方法:
loadBeanDefinitions 做了层封装,主要工作是在 doLoadBeanDefinitions 实现的。doLoadBeanDefinitions 方法主要做了两件事:
- 将 Resource 解析为 Document 对象。
- 从 Document 解析和注册 BeanDefinition。
第一步只是工具人,不必深究。
关键在于第二步,也比较复杂,后文再分析吧,毕竟源码读起来还是有点累……本文就先到这了。
- Spring 源码中的关键部分通常是有点小规律的,比如:
- 以 try...catch 包围
- 以 do 开头的方法才是实际做事的,前面都是 caller,比如 doLoadBeanDefinitions、doGetBean 等
- 其他规律小伙伴们可以继续补充。
小结
本文开始进入 Spring IoC 容器的源码了,主要找到了一条主线,为便于对整体有个了解,这里简单总结了一个思维导图(还有后面的预告):
小伙伴们读的时候也可以用思维导图工具画一下主要流程,起到提纲挈领的作用,也便于以后回顾。
源码读起来还是需要一点耐心的,毕竟它就是这么朴实无华……且枯燥🤣
