当今,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 之前都需要完成哪些准备工作。