转载

【超详细的Spring源码分析 —— 01 Spring的宏观上的执行流程 】

当今,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 实例的时候完成了自动类型转换的工作。 除此以外,还有循环依赖检测等诸多行为,这些都是后话了。

二、IoC 执行流程分析

我们知道 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 容器的背后,大致能够划分为几个步骤了,这几个步骤分别是:

  1. 读取资源
  2. 创建注册器
  3. 创建读取器
  4. 解析资源,组装 bean 到注册器

除此以外,Spring 对于 Bean 管理的核心组件,也基本上能够明确了:

  • 资源抽象 - Resource
  • 注册器,也可称之为工厂 - DefaultListableBeanFactory
  • 读取器 - BeanDefinitionReader

下一篇文章,我将会针对上述的 1~3 步骤进行详细分析,看看 IoC 容器在解析并装配 Bean 之前都需要完成哪些准备工作。

原文  https://juejin.im/post/5eff0b5e6fb9a07eb65a53f0
正文到此结束
Loading...