type
status
date
slug
summary
tags
category
icon
password
Imspring 源码介绍(一):基本概念
Imspring 项目地址点这里

概述

Imspring-core 模块围绕 ApplicationContext 和 Beanfactory 两个 Bean 工厂实现了 Spring 的核心 IOC 功能。整个模块涉及的核心知识点如下:
  • Bean 的全生命周期。(重点)
  • ApplicationContext 和 BeanFactory 的初始化流程。(重点)
  • 包名扫描和加载。
  • 依赖注入,以及三级缓存解决循环依赖问题。
  • 使用 BeanFactoryPostProcessor 实现定制化 Bean 工厂(Configuration 配置类的基础)
  • 使用 BeanPostProcessor 实现定制化 Bean。(AOP的基础)
  • 配置文件加载和环境切换。
为了让大家更加清楚整个流程,先介绍下 Spring 的一些基础概念和组件。

基础概念

ApplicationContext 和 BeanFactory

这两个是 Spring 的核心概念。通俗易懂地解释,BeanFactory 是一个工厂接口,提供 IOC 的基本功能,包括读取 Bean 的 XML 配置文件,管理 Bean 的加载和实例化,维护 Bean 之间的依赖关系,管理 Bean 的生命周期等。这个接口大概长这样
这个接口的核心方法就是使用 getBean() 方法获取各种 Bean。我们并不需要关心这些 Bean 是怎么构造出来,只需要直接从容器取就可以了,这也符合工厂模式的理念。ApplicationContext 继承了 BeanFactory 接口,具备 BeanFactory 接口的全部功能,而且提供更丰富的特性。
由上面看出 ApplicationContext 持有 BeanFactory 的实例,所以具备 BeanFactory 的全部功能,并提供更完整的框架功能。两者的区别可以用下面表格来表示。
功能
BeanFactory
ApplicationContext
Bean 实例化时机
BeanFactroy 采用的是延迟加载形式来注入 Bean 的,即只有在使用到某个 Bean 时(调用getBean()),才对该 Bean 进行加载实例化。
ApplicationContext 采用预加载的方式注入 Bean,在启动的时候就会调用 refresh() 预加载所有单例 Bean,完成整个环境的初始化。
Bean 扩展
BeanPostProcessor 和 BeanFactoryPostProcessor 手动注册
BeanPostProcessor 和 BeanFactoryPostProcessor 自动注册
上下文
从 XML 文件中读取上下文(XmlBeanFactory)
支持通过 ClassPath、FileSystem、Web、Annotation 等方式读取上下文,并且可以设置多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次。
国际化
/
支持国际化 i18n( MessageSource)
访问资源
/
提供统一的资源文件访问方式,加载多种不同类型的资源(ResourceLoader)
环境切换
/
支持多环境切换,加载多个配置文件(Environment)
事件机制
/
强大的事件监听机制,当 ApplicationContext 中发布一个事件时,所有扩展ApplicationListener 的 Bean 都将接受到这个事件,并进行相应的处理。(ApplicationEvent 和 ApplicationListener)
总结:
  • BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身。
  • ApplicationContext 提供更多面向实际开发的功能,几乎所有场合都可以直接使用,面向使用 Spring 的开发者。日常使用建议直接使用 ApplicationContext。

Bean 生命周期

notion image
Bean 的生命周期是指一个对象在 Spring 容器里面从创建到被销毁的整个过程。这里引用一个网上非常经典的图,这个图基本涵盖了 Bean 从初始化到被销毁的整个生命周期。为了方便记忆,可以分为四个阶段:
  1. 实例化(Instantiation)
  1. 属性赋值(Populate)
  1. 初始化(Initialization)
  1. 销毁(Destruction)
notion image
Imspring 项目严格遵循以上 Bean 生命周期的顺序,最后为了简化跳过了销毁阶段。

Resource 和 ResourceLoader

Spring 用 Resource 接口抽象所有的底层资源,包括 File、ClassPath、URL 等。Spring 通过 Resource 接口将对物理资源的访问方式统一为 URL 格式和 Ant 风格带通配符的资源地址。
Spring 提供多种 Resource 的实现类,我们可以直接使用。常用的有:
  • FileSystemResource:针对 java.io.File 的 Resource 实现类。可以消除操作系统底层差异,对不同的操作系统使用同一的 API 来访问。
  • ClassPathResource:访问类加载路径下的资源,对于 WEB 应用可以自动搜索位于 WEB-INF/classes 下的资源文件,是 Spring 中是非常常用的一种资源类型。
  • UrlResource:java.net.URL 的 Resource 实现类。可以访问网络资源,也可以访问本地资源。支持 http、https、file、ftp、jar 等协议。
ResourceLoader 接口是 Resource 的加载器,用于快速加载一个 Resource 资源对象。Spring 中的默认实现是 DefaultResourceLoader
简单来说,Spring 通过 Resource 和 ResourceLoader 对外提供统一的资源访问方式

BeanDefinition 和 BeanDefinitionRegistry

BeanDefinition 是用来描述 Bean 的元数据信息。除了我们熟悉的 Bean 的 Class 对象之外,还保存了 Bean 是否是单例、Bean 是否在容器中是懒加载、Bean 在容器中的名字等信息。Spring 依赖这些信息来完成一个 Bean 的实例化。
简单来说,Spring 将 Bean 的定义和 Bean 的实例分开处理。首先将类文件 Class 解析为 BeanDefinition,然后再根据 BeanDefinition 的定义,在合适的时机(例如延迟创建)创建正确类型的 Bean 实例(单例或原型)。这个 Bean 实例就是我们日常使用的 Bean。
BeanDefinitionRegistry 可以理解为 BeanDefinition 的容器接口,完成 BeanDefinition 的注册、移除和删除等功能。Spring 的 BeanFactory 默认实现类 DefaultListableBeanFactory 实现了 BeanDefinitionRegistry 接口,使用 beanDefinitionMap 集合来存放所有的 BeanDefinition。
Spring 中命名为 XXXRegistry 的接口或者类,在 Spring 中通常扮演注册中心的职责,维护管理对应的一组实例。

SingletonBeanRegistry

上面谈到 Spring 中 Bean 的定义和 Bean 的实例是分开处理。我们日常使用 Spring 的时候,99% 的场景都是使用 Spring Bean 的单例实例。SingletonBeanRegistry 相当于 Bean 单例实例的容器接口,提供了统一访问单例 Bean 的功能
Spring 中 SingletonBeanRegistry 的 默认实现类是 DefaultSingletonBeanRegistry。这个类非常非常的重要,可以认为是 Spring IOC 容器的核心内容,大名鼎鼎的三级缓存就放在这里面。除此之外还有非常多的缓存用于提高性能,并且做的事情也很多,比如解决 Bean 的循环依赖问题。

BeanPostProcessor 和 BeanFactoryPostProcessor

BeanFactoryPostProcessor 和 BeanPostProcessor 这两个接口,都是 Spring 初始化 Bean 对外暴露的扩展点。在这之前我们先明确两个概念,一个是 Instantiation ,一个是 Initialization
  • Instantiation :实例化 ,就是创建 Bean 的过程(比如调用构造函数)。
  • Initialization :初始化, 就是对 Bean 进行赋值(比如调用 setter 方法)、配置属性的过程。
BeanFactoryPostProcessor 是针对 BeanFactory 的扩展,是在 Bean 实例化(Instantiation)之前调用的。根据名称可以看出是对 BeanFactory 的修改,比如修改 Bean 的定义或者作用域等,从而影响 Bean 实例的创建。
这个接口有一个很重要的实现类 ConfigurationClassPostProcessor。这个类负责解析带 @Configuration 注解的配置类,把 @Bean 修饰的方法和 @Import 引入的类加载成 Bean 注入到容器。除此之外还可以解析 @ComponentScan,扫描指定路径下的类文件,筛选出需要加载的 Bean。这个类是 Spring 实现自动配置的关键。
BeanPostProcessor 是针对 Bean 的扩展,这时候 Bean 实例化(Instantiation)已经完成了,准备开始初始化(Initialization)。初始化过程包括以下几个动作:
  1. 执行 BeanPostProcessor#postProcessBeforeInitialization,也就是常说的前置处理。
  1. 执行 InitializingBean#afterPropertiesSet。
  1. 执行 init-method(@PostConstruct 修饰的方法) 。
  1. 执行 BeanPostProcessor#postProcessAfterInitialization,也就是常说的后置处理。
由此看出 BeanPostProcessor 主要是在初始化前后完成对 Bean 实例的一些修改,比如为 Bean 实例创建代理对象。
BeanPostProcessor 有一个非常重要的子接口:InstantiationAwareBeanPostProcessor,这个接口定义的方法主要是在 Bean 实例化(Instantiation)前后执行。
BeanPostProcessor 和 InstantiationAwareBeanPostProcessor 这两个扩展点非常重要,是 AOP 的基础。这两个接口中的方法对容器中所有的 Bean 都生效,在每个 Bean 实例化之后都会调到这两个接口的方法,判断该 Bean 是否满足创建代理对象的条件,如果满足的话就创建并返回代理对象,这就是 AOP 的原理。上面的过程,可以使用下面的流程图来描述。
notion image

Environment、Profile 和 Property

Spring 中的 Environment 相当于一个外部资源的容器。Spring 在启动的时候会把所有外部配置源中的属性,包括本地配置文件、远程配置文件(比如 Nacos、Apollo),都加载到 Environment 中保存起来。
Environment 再进行进一步细分的话,分为 Property 和 Profile 两部分
  • Property:配置项,本质是配置文件的一组键值对配置信息。在项目中会把配置项从代码中分离出来,这样的好处在于修改起来很容易,只需修改下配置文件或命令行参数,然后重启一下就可以了。
  • Profile:配置环境。大多数项目都有多套配置对应多个环境,一般来说有开发环境、测试环境和生产环境,这里的环境就是 Profile。程序读取 Profile 的值,根据 Profile 的不同展示不同的特性。
从本质上讲,Profile 也是属于 Property 的一种,作用是负责环境的切换。只是这个配置太重要了,也比较特殊,所以作为一个单独的概念来处理。
简单总结,Environment = Property + Profile
既然涉及到属性读取,我们肯定需要一个工具从外部配置源读取文件,PropertyResolver 接口承担了这个角色,负责解析外部配置源,加载和获取 Property。Environment 接口继承了 PropertyResolver接口,提供了 Profile 的相关功能,用于加载和获取 Profile。
Spring 提供多个 Environment 的实现,StandardXXX 开头的是标准实现,其中最常用的就是 StandardServletEnvironment,负责加载 Web 应用的环境信息,例如从 WEB-INF/web.xml 加载 servlet 上下文等。
Environment 本身也会被保存为容器中的一个 Bean 实例。绝大数情况下,bean 都不需要直接访问 Environment 实例,而是通过类似 @Value 注解的方式把属性值注入进来。
如果后面项目中实在需要用到 Environment,可以手动将 Environment 实例注入到该业务 Bean 中,也可以实现 EnvironmentAware 接口,得到 Environment 类型的 Bean 实例之后可以通过 getProperty() 获取具体属性值。

总结

  • BeanFactory 是 Spring 的容器工厂接口,提供 IOC 的基本功能。ApplicationContext 继承了 BeanFactory 接口,具备 BeanFactory 的全部功能并提供更丰富的特性。
  • Bean 生命周期是指一个对象在 Spring 容器里面从创建到被销毁的整个过程,包括:实例化(Instantiation)、属性赋值(Populate)、初始化(Initialization)和销毁(Destruction)四个阶段。
  • Resource 接口把所有的底层资源抽象为统一的类,通过 URL 格式和 Ant 风格带通配符的资源地址进行访问。ResourceLoader 接口是 Resource 的加载器,用于快速加载一个 Resource 资源对象。
  • Spring 中 Bean 的定义和 Bean 的实例是分开处理的。
    • BeanDefinition 相当于 Bean 的定义,用来描述 Bean 的元数据信息。Spring 依赖这些元数据来完成一个 Bean 的实例化。BeanDefinitionRegistry 是 BeanDefinition 的容器接口,完成 BeanDefinition 的注册、移除和删除等功能。
    • SingletonBeanRegistry 是 Bean 单例实例的容器接口,提供了统一访问单例 Bean 的功能。
  • BeanFactoryPostProcessor 和 BeanPostProcessor 这两个接口,都是 Spring 初始化 Bean 对外暴露的扩展点,负责在 Bean 实例化(Instantiation)和初始化(Initialization)前后对 Bean 实例进行增强操作。
  • Environment 是 Spring 存放外部资源的容器。Spring 在启动的时候会把所有外部配置源中的属性,包括本地配置文件、远程配置文件(比如 Nacos、Apollo),都加载到 Environment 中保存起来。Environment = Property + Profile。
    • Property:配置项,本质是配置文件的一组键值对配置信息。
    • Profile:配置环境,通过切换 Profile 显示不同环境的特性。
上面这些就是 Spring 源码的核心概念,包括一些重要的实现类也列了出来。记不住这些概念也没关系,先大概有个印象,在源码里面这些概念和类会反复出现,不断加深印象。
Imspring 源码介绍(二):IOC 实现Linux系列:文件系统
mcbilla
mcbilla
一个普通的干饭人🍚
Announcement
type
status
date
slug
summary
tags
category
icon
password
🎉欢迎来到飙戈的博客🎉
-- 感谢您的支持 ---
👏欢迎学习交流👏