当今,SpringBoot、SpringCloud 盛行,而无论怎么发展,背后的根基依然是 Spring。
为了加深自己对 Spring 的理解,笔者打算针对 Spring 源码分析,写下自己的见解与心得,也是把我的一点微薄知识分享给大家。
好了废话不多说,我们先从最初的起点开始。
Spring 最原始地配置Bean元数据的方式,是通过 xml 进行配置,这里先配置一个 Student 类:
public class Student { private String name; private int age; } 复制代码
<bean id="student" class="com.whl.spring.beans.Student"> <property name="name" value="whl" /> <property name="age" value="18" /> </bean> 复制代码
这个bean的定义比较简单,这里就不再赘述了,但有一个小细节可以了解一下:我们在 xml 中配置的 student.age
为字符串类型,而 pojo 类上却是 int 类型。这说明了 Spring 在根据元数据创建 Bean 实例的时候完成了自动类型转换的工作。
除此以外,还有循环依赖检测等诸多行为,这些都是后话了。
我们知道 Spring 会把创建好的 Bean 放在 IoC 工厂,那么这个工厂又是如何创建的呢?
最初我的理解是这样的:ApplicationContext 这个上下文对象即是一个 IoC 工厂,并在 BeanFactory 基础之上增加了许多特性。
public class SpringClient { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); } } 复制代码
这么理解虽然也没有什么错(我个人认为是没什么毛病),但还不够深入。
其实在简单的 new ClassPathXmlApplicationContext("applicationContext.xml")
背后,Spring 背后还封装了资源抽象、创建注册器、创建读取器、以及通过读取器解析资源并将 Bean 组装到注册器的工作,整个流程用代码来表述如下所示:
public class SpringClient { public static void main(String[] args) { // 1. 首先是创建资源 // spring 将不同类型的 Resource 进行了抽象, 针对不同类型的资源都给出了不同的实现: // 比如最常见的 classpath 下的资源 Resource resource = new ClassPathResource("applicationContext.xml"); // 又比如文件系统中的资源 // Resource resource = new FileSystemResource("~/.../applicationContext.xml"); // ... // 还有诸多针对不同类型资源的实现 // 2. 创建工厂(也可以理解为创建 BeanDefinition 注册器) // 创建一个默认的、可列举的Bean工厂, 这个工厂需要引入元数据资源才能创建bean // 这个工厂实现了 BeanDefinitionRegistry 接口, 也可以认为它是一个注册器 DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory(); // 3. 创建 BeanDefinition 读取器 // 这个 BeanDefinitionReader 也是一个接口, 分别对不同的资源类型, 比如 properties、groovy、xml 进行了不同的实现 // 由于我们的 Resource 是基于xml的, 因此创建一个支持xml的读取器 // 这个读取器在构造时需要引入一个 BeanDefinitionRegistry 类型的实例 BeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory); // 4. 通过读取器读取资源, 完成bean装配 // 它会根据 resource 中定义的元数据生成 bean, 然后组装到 defaultListableBeanFactory 中 // 当bean装配完成后, 读取器的使命也就结束了 // 提前预告一下, 这里是整个 Ioc 流程中最关键、最复杂的一环 beanDefinitionReader.loadBeanDefinitions(resource); // 5. 然后我们通过工厂来获取到相应的示例, 这个过程就是所谓的依赖注入 - DI Student student = defaultListableBeanFactory.getBean("student", Student.class); } } 复制代码
现在我们应该清楚,在创建 IoC 容器的背后,大致能够划分为几个步骤了,这几个步骤分别是:
除此以外,Spring 对于 Bean 管理的核心组件,也基本上能够明确了:
下一篇文章,我将会针对上述的 1~3 步骤进行详细分析,看看 IoC 容器在解析并装配 Bean 之前都需要完成哪些准备工作。