这个问题松哥想了很久要怎么和大家展开介绍,最早我是想整一篇文章把 Spring 容器整个从头到尾捋一遍,但是,东西太多显然不现实,我还是倾向于通过不同的文章,从不同的角度来介绍 Spring 容器的一些使用细节,最后再将整体串通起来~
因此,今天我想先和大家聊一个小的话题,就是我们先来捋一捋 Spring 中真正干活的容器到底是哪个?
说到 Spring 容器,我们很容易想到 BeanFactory,大家很容易拿到这张图:
这张图大致上一看,有七八个能干活的容器,难道 Spring 中真的有这么多种不同的容器吗?那我们不妨想一想,容器的核心职责是什么?我们是否真的需要这么多容器?
其实,Spring 中,跟 Bean 最核心的功能相关的容器,只有三个!接下来我们来分析一下。
1. BeanFactory
BeanFactory 是所有容器的根,它里边提供了规范了最基本的容器方法和判断方法,例如如下一些方法:
- getBean(String name):根据给定的 Bean 名称获取对应的实例对象。此方法允许通过名称检索 Bean,如果找到匹配的 Bean,则返回该 Bean 的实例。
- getBean(String name, Class
requiredType):根据给定的 Bean 名称和类型获取对应的实例对象。此方法允许通过名称和类型检索 Bean,如果找到匹配的 Bean,则返回该 Bean 的实例。 - getBean(Class
requiredType):根据给定的类型获取对应的实例对象。此方法允许通过类型检索 Bean,如果找到匹配的 Bean,则返回该 Bean 的实例。 - getBean(Class
, Object…):根据给定类型返回对应的实例对象,第二个参数是在构造给定的实例时构造方法所使用的参数。 - getBean(String, Object…):这个跟上一个方法类似,区别在于第一个参数传入的是 Bean 名称。
- getBeanProvider(Class
):这个的作用是获取一个用于访问和管理 Bean 的提供者(BeanProvider)。BeanProvider 是一个泛型接口,它提供了一种延迟加载和按需获取 Bean 的机制。通过 getBeanProvider 方法,可以获取一个 BeanProvider 对象,然后可以使用该对象来获取特定类型的 Bean 实例。BeanProvider 具有延迟加载、按需获取以及安全访问等功能,这是 Spring5.1 刚刚推出的方法,所以大家目前来说应该见到的并不多。 - getBeanProvider(ResolvableType):这个等同于上面的方法,只不过传入的参数类型有差别。
- getType(String name):获取指定名称的 Bean 的类型。如果找到匹配的 Bean,则返回 Bean 的类型;如果找不到匹配的 Bean,则返回 null。
- getType(String, boolean):这个作用也是等价于上面这个方法,不同的是,多了一个 boolean 类型的参数,表示在获取类型的时候,是否提前将 FactoryBean 初始化,在之前的文章FactoryBean 和它的兄弟SmartFactoryBean!中松哥和大家聊了 FactoryBean 具备天然的延迟加载特性,这个方法也是 Spring5.2 开始才有的。
- containsBean(String name):检查容器中是否包含指定名称的 Bean。如果容器中存在具有给定名称的 Bean,则返回 true,否则返回 false。
- isSingleton(String name):检查指定名称的 Bean 是否为单例。如果指定名称的 Bean 是单例,则返回 true,否则返回 false。
- isPrototype(String name):检查指定名称的 Bean 是否为原型。如果指定名称的 Bean 是原型,则返回 true,否则返回 false。
- getAliases(String name):获取给定名称的 Bean 的所有别名。如果存在别名,则返回别名的数组;如果不存在别名,则返回空数组。
- isTypeMatch(String,ResolvableType):这个方法是判断给定的 Bean 名称是否和给定的类型相匹配。
- isTypeMatch(String,Class<?>):作用等价于上面这个方法。
BeanFactory 中常见的方法主要就是这些。这也是容器操作中最最核心的方法。
在本文一开始的类继承图中,BeanFactory 有很多实现类,那么是哪些实现类实现了上面这些方法呢?其实只有三个,分别是:
- DefaultListableBeanFactory
- StaticListableBeanFactory
- SimpleJndiBeanFactory
别看实现类很多,但是其实干活的就是这三个。
1.1 DefaultListableBeanFactory
DefaultListableBeanFactory 是 Spring 框架中最常用的 bean 工厂之一。它是 BeanFactory 接口的一个实现类,同时也是 ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory 接口的实现类。DefaultListableBeanFactory 提供了丰富的功能来管理和访问 bean。
DefaultListableBeanFactory 的主要特点和功能包括:
- Bean 定义的注册和获取:DefaultListableBeanFactory 可以通过注册 BeanDefinition 对象来定义和配置 Bean。它提供了方法来注册、获取和移除 Bean 的定义。这使得我们可以在运行时动态地添加、修改和删除 Bean。
- Bean 的实例化和获取:DefaultListableBeanFactory 负责实例化和管理 Bean 的生命周期。它可以根据 Bean 的定义创建 Bean 的实例,并提供方法来获取 Bean 的实例。它支持单例、原型和其他作用域的 Bean。
- 依赖注入:DefaultListableBeanFactory 支持自动装配和显式依赖注入。它可以解析 Bean 之间的依赖关系,并在需要时自动注入依赖。它支持构造函数注入、属性注入和方法注入。
- AOP 支持:DefaultListableBeanFactory 支持面向切面编程 (AOP)。它可以通过配置切面和通知,实现横切关注点的模块化。
- Bean 生命周期管理:DefaultListableBeanFactory 提供了对 Bean 生命周期的管理。它可以在 Bean 初始化之前和销毁之后执行相应的回调方法。
- Bean 列表和查询:DefaultListableBeanFactory 实现了 ListableBeanFactory 接口,提供了一系列方法来获取 Bean 的列表信息。它支持按名称、类型和条件进行 Bean 的查询和获取。
总的来说,DefaultListableBeanFactory 是 Spring 框架中最常用的 Bean 工厂,它提供了丰富的功能来定义、配置、管理和访问 Bean,它的功能最完整也最成熟。它是实现依赖注入和控制反转 (IoC) 的核心组件之一。
我们平时开发用的各种“容器”其实都是 DefaultListableBeanFactory。
例如在松哥之前和大家讲的基本用法中,我们常用的两个类分别是 ClassPathXmlApplicationContext 和 AnnotationConfigApplicationContext,这两个底层使用的容器其实就是 DefaultListableBeanFactory。
1.2 StaticListableBeanFactory
StaticListableBeanFactory 这个容器我们其实很少用到,但是这里大家也了解一下。
这个容器从名字上就能看出其特点:静态。不错,这是一个静态容器,它里边定义了一个 Map 集合,当你想要存储一个 Bean 的时候,它就给你存到这个 Map 集合中,当你想要获取一个 Bean 的时候,它就给你从这个 Map 集合中读取出来。
StaticListableBeanFactory 的主要特点和功能包括:
- 静态:StaticListableBeanFactory 在初始化时加载所有的 Bean 定义,并在运行时保持不变。这意味着它不支持在运行时动态修改或删除 Bean,适用于那些不需要动态修改 Bean 的场景,动态添加新的 Bean 是可以的。
- 列表化:StaticListableBeanFactory 实现了 ListableBeanFactory 接口,提供了一系列方法来获取 Bean 的列表信息。它可以通过名称、类型、注解等方式来获取 Bean,还支持按条件过滤和排序。
- 高效访问:由于所有的 Bean 定义在初始化时就加载完成,StaticListableBeanFactory 提供了高效访问 Bean 的能力。它不需要在运行时进行 Bean 的查找和创建,从而提供了更快的性能。
- 配置方式:StaticListableBeanFactory 可以通过配置文件或编程方式进行配置。它支持使用 XML、注解或 Java 配置类来定义和配置 Bean。
总的来说,StaticListableBeanFactory 提供了一种静态且列表化的方式来管理和访问 Bean。它适用于那些在运行时不需要动态修改 Bean 的应用场景,并且提供了方便的方法来获取和操作 Bean 的列表信息。
1.3 SimpleJndiBeanFactory
SimpleJndiBeanFactory 是 Spring 框架中的一个类,它是 BeanFactory 接口的一个实现类。SimpleJndiBeanFactory 使用简单的 JNDI (Java Naming and Directory Interface) 技术来管理和访问 Bean。
JNDI 是 Java 平台提供的一种标准 API,用于访问命名和目录服务。
SimpleJndiBeanFactory 利用 JNDI 的功能,可以从 JNDI 中查找和获取 Bean,以及执行与 JNDI 相关的操作。
SimpleJndiBeanFactory 的主要特点和功能包括:
- JNDI 支持:SimpleJndiBeanFactory 支持使用 JNDI 技术来管理和访问 Bean。它可以从 JNDI 中查找和获取 Bean,以及执行与 JNDI 相关的操作,如绑定、解绑等。
- 简单易用:SimpleJndiBeanFactory 提供了一种简单易用的方式来访问 JNDI 中的 Bean。它封装了 JNDI API 的复杂性,提供了更简洁的接口和方法。
- 配置方式:SimpleJndiBeanFactory 可以通过配置文件或编程方式进行配置。它支持使用简单的 properties 文件来配置 JNDI 上下文和 Bean 的绑定关系。
- 跨平台性:由于 JNDI 是 Java 平台的标准 API,SimpleJndiBeanFactory 具有良好的跨平台性,可以在不同的 Java 环境中使用。
老实说,这个 SimpleJndiBeanFactory 最大的特点就是利用了 JNDI 技术,然而,我们平时开发实际上很少会用到 JNDI 技术,所以这个容器其实用的并不多。
Spring 中的基础容器就这三个,其中被大量使用的是第一个 DefaultListableBeanFactory。
2. ApplicationContext
如果前面所说的三个容器(BeanFactory)是一个发动机,那么 ApplicationContext 就是一辆汽车,回顾本文一开始的类结构图,其实除了前面三个 BeanFactory 之外,其他的都算是 ApplicationContext 了。
BeanFactory 提供了 Bean 的核心功能,但是依然缺乏很多东西,例如配置文件要怎么加载?带注解的类谁去扫描?国际化怎么实现?事件的处理等等。。。这些功能上面三个核心类都没有!那么谁有呢?那就是 ApplicationContext 有。另外,Spring 中许多通过 BeanPostProcessor 实现的功能,通过 BeanFactory 也无法实现,因为直接使用 BeanFactory 并不会触发 BeanPostProcessor,如果非要使用,得自己手动去触发这些后置处理器的执行。
ApplicationContext 并非一个全新的容器,本质上,它里边使用的是 DefaultListableBeanFactory 容器,只不过在该容器的基础上继续增强了许多功能。
在 ApplicationContext 中,容器的初始化都是从 refresh 方法开始,该方法会调用 obtainFreshBeanFactory 方法去获取一个 ConfigurableListableBeanFactory 类型的容器,问题是 ConfigurableListableBeanFactory 只有一个实现类,那就是 DefaultListableBeanFactory,所以我们说,在 ApplicationContext 中使用的都是 DefaultListableBeanFactory 容器。
因此,在具体实践中,如果能够使用 ApplicationContext,就尽量不要直接使用 DefaultListableBeanFactory 或者其他容器,除非你想完全控制容器的运行。
下面这张表格列出来了 BeanFactory 和 ApplicationContext 之间的一些差别。
特性 | BeanFactory | ApplicationContext |
---|---|---|
Bean 的实例化/注入 | 支持 | 支持 |
Bean 生命周期管理 | 不支持 | 支持 |
自动触发 BeanPostProcessor | 不支持 | 支持 |
自动触发 BeanFactoryPostProcessor | 不支持 | 支持 |
国际化(I18N) | 不支持 | 支持 |
内置 ApplicationEvent 发布机制 | 不支持 | 支持 |
好啦,这样梳理一下,容器应该就好理解很多了,接下来的文章松哥会通过具体的案例,来和小伙伴们演示不同 BeanFactory 以及 ApplicationContext 的用法。