Spring - IoC 源码阅读笔记:基础概念

in Tech Java

前言

本篇文章主要介绍IoC容器相关概念及核心启动流程。

简介

IoC 和 DI/DL

IoC (Inversion of Control),即控制反转。在传统的程序设计,我们直接在对象内部通过 new 来创建对象,是主动去创建依赖对象;而在 Spring中有专门的一个容器来创建和管理这些对象,并将该对象依赖的其他对象注入到该对象中,这个容器我们一般称为 IoC 容器。所有的类的创建、销毁都由 Spring 来控制,也就是说控制对象生存周期的不再是引用它的对象,而是Spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被 Spring 控制,所以这叫控制反转。而 DI/DL 则是 IoC 两种不同的实现方式。

DI (Dependency Injection),即依赖注入。可以认为 IoC 和 DI 其实是同一个概念的不同角度描述。依赖注入是指组件之间的依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

DL (Dependency Lookup),即依赖查找。需要主动使用 API 进行查找资源和组装对象。是一种更加传统的 IoC 实现方式,因为具有侵入性,已经被抛弃。

Bean

由 Spring IoC 容器管理的对象称为 bean。 bean 是一个由 Spring IoC 容器实例化,组装和管理的对象。我们经常会在 Service 上使用 @Service 注解,然后在要使用该 Service 的类中通过 @Autowire 注解来注入,这个 Service 就是一个 bean。在这个地方,@Service 注解相当于告诉 IoC 容器:这个类你需要帮我创建和管理;而 @Autowire 注解相当于告诉 IoC 容器:我需要依赖这个类,你需要帮我注入进来。

BeanDefinition

BeanDefinition 包含称为配置元数据的信息。每个 BeanDefinition 的均包含下列属性。

属性 描述
class 这个属性是强制性的,并且指定用来创建 bean 的类。
name 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。
scope 这个属性指定由特定的 BeanDefinition 创建的对象的作用域。
constructor-arg 基于构造方法的形式注入依赖。
properties 基于properties的形式注入依赖。
autowiring mode 自动装配模型,也就是无需再显示指定constructor-arg或properties。
lazy-initialization mode 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。
initialization 方法 在 bean 的所有必需的属性被容器设置之后,调用回调方法。
destruction 方法 当包含该 bean 的容器被销毁时,使用回调方法。

Spring 支持 Bean 的作用域(scope)

作用域 描述
singleton 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值
prototype 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean()
request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境
global-session 一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境

Spring Beans 自动装配(autowiring mode)

模式 描述
no 这是默认的设置,它意味着没有自动装配,需要显式引用 bean 来连线。
byName 由属性名自动装配。
byType 由属性数据类型自动装配。存在多个相同类型的bean,则会抛出异常。
constructor 类似于 byType,仅适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则会抛出异常。
autodetect Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。

BeanPostProcessor

Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理。BeanPostProcessor ​接口定义回调方法postProcessBeforeInitialization(Object, String)、postProcessAfterInitialization(Object, String),实现该方法来提供自己的实例化逻辑,依赖解析逻辑等。也可以在 ​Spring​ 容器通过插入一个或多个 ​BeanPostProcessor​ 的实现来完成实例化,配置和初始化一个​bean​之后实现一些自定义逻辑回调方法。

BeanFactoryPostProcessor

BeanFactory 后置处理器允许在容器运行前进行额外的处理,例如修改 bean 属性值,实现 bean 动态代理等。很多框架都是通过此接口实现对 Spring IoC 容器的扩展,例如 mybatis 与 spring 集成时,只定义了 mapper 接口,无实现类,但 spring 却可以完成自动注入。

BeanFactory

基础类型 IoC 容器,提供完整的 IoC 服务支持。如果没有特殊指定,默认采用延迟初始化策略( lazy-load)。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需要的资源有限。对于资源有限,并且功能要求不是很严格的场景, BeanFactory 是比较合适的 IoC 容器选择。

BeanFactory的对象注册与依赖绑定方式:

1. 直接编码方式。
也就是直接以编写代码的行形式来完成对象注册与绑定。当然了,不管什么方式,最终都需要编码才能“落实”所有信息并付诸使用。

2. 外部配置文件方式。
有两种,一种是 Properties 配置格式的加载;一种是 XML 配置格式的加载。其中 Properties 几乎没什么人用了,书写麻烦。XML 配置格式是 Spring 支持最完整,功能最强大的表达方式。但是现如今 SpringBoot 自动配置横行时代,XML 配置也逐步减少使用了。

3. 注解方式。 
注解方式是目前最常用的。
提供 Bean 注册的常见注解有:@Controller、@Service、@Repository、@Component、@Bean。
提供 Bean 依赖绑定的常见注解有:@Autowired、@Qualifier、@Resource。
更多注解参阅 Spring Boot项目常用注解

ApplicationContext

在 BeanFactory 的基础上构建,是相对比较高级的容器实现,除了拥有 BeanFactory 的所有功能,还提供了其他高级特性,比如:事件发布、国际化信息支持、统一资源加载策略等。 ApplicationContext 所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。所以,相对于 BeanFactory 来说, ApplicationContext 要求更多的系统资源,同时,因为在启动时就完成所有初始化,容器启动时间较之 BeanFactory 也会长一些。在那些系统资源充足,并且要求更多功能的场景中, ApplicationContext 类型的容器是比较合适的选择。

最常被使用的 ApplicationContext 接口实现:

1. WebXmlApplicationContext
该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。
2. ClassPathXmlApplicationContext
该容器从 XML 文件中加载已被定义的 bean。只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。
3. FileSystemXmlApplicationContext
该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。
4. AnnotationConfigApplicationContext
该容器从注解中加载已被标记的 bean。

FactoryBean

FactoryBean 是 Spring 容器提供的一种可以扩展容器对象实例化逻辑的接口。也就是说,它本身与其他注册到容器的对象一样,只是一个 Bean 而已,只不过,这种类型的Bean本身就是生产对象的工厂(Factory)。如果我们想自己实现 bean 的创建操作,那么就去实现 FactoryBean 接口,并实现它的 getObject() 方法。

ApplicationContextInitializer

ApplicationContextInitializer 是在 spring 容器刷新之前执行的一个回调接口。也就是在 ConfigurableApplicationContext#refresh() 方法执行之前,允许我们对 ConfigurableApplicationContext 的实例做进一步的处理。其中包括添加 BeanFactoryPostProcessor 、 ApplicationListener 等等。同时, ApplicationContextInitializer 支持 Order 注解,表示执行顺序,越小越早执行。

ApplicationListener

ApplicationListener 是基于观察者模式实现的,在不同的运行点,会推送相应的事件。通过 ApplicationEvent 类和 ApplicationListener 接口,可以接收容器推送的事件,进而做出相应的处理。内置的事件:ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent。

核心流程

容器构建启动的入口有很多,这里以目前最流行的 SpringBoot 方式来讲。