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 生命周期
Bean 的生命周期是指一个对象在 Spring 容器里面从创建到被销毁的整个过程。这里引用一个网上非常经典的图,这个图基本涵盖了 Bean 从初始化到被销毁的整个生命周期。为了方便记忆,可以分为四个阶段:
- 实例化(Instantiation)
- 属性赋值(Populate)
- 初始化(Initialization)
- 销毁(Destruction)
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)。初始化过程包括以下几个动作:
- 执行 BeanPostProcessor#postProcessBeforeInitialization,也就是常说的前置处理。
- 执行 InitializingBean#afterPropertiesSet。
- 执行 init-method(@PostConstruct 修饰的方法) 。
- 执行 BeanPostProcessor#postProcessAfterInitialization,也就是常说的后置处理。
由此看出 BeanPostProcessor 主要是在初始化前后完成对 Bean 实例的一些修改,比如为 Bean 实例创建代理对象。
BeanPostProcessor 有一个非常重要的子接口:InstantiationAwareBeanPostProcessor,这个接口定义的方法主要是在 Bean 实例化(Instantiation)前后执行。
BeanPostProcessor 和 InstantiationAwareBeanPostProcessor 这两个扩展点非常重要,是 AOP 的基础。这两个接口中的方法对容器中所有的 Bean 都生效,在每个 Bean 实例化之后都会调到这两个接口的方法,判断该 Bean 是否满足创建代理对象的条件,如果满足的话就创建并返回代理对象,这就是 AOP 的原理。上面的过程,可以使用下面的流程图来描述。
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 源码的核心概念,包括一些重要的实现类也列了出来。记不住这些概念也没关系,先大概有个印象,在源码里面这些概念和类会反复出现,不断加深印象。
- Author:mcbilla
- URL:http://mcbilla.com/article/83fdecde-8a77-4cee-954d-6e012a8b97d5
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts