声明:我已委托「维权骑士」(rightknights.com)为我的文章进行维权行动。
导言
通过上一章《理解Spring系列——什么是控制反转(Inversion of Control, IoC)》,我们了解到IoC编程模式的本质,通过协议约定来分离when-to-do和what-to-do两个关注点,实现模块间的解耦。
而在Spring Framework中,BeanFactory绝对是Spring IoC的核心担当:BeanFactory 在Spring中的主要作用,通过抽象Bean实例化的具体过程(体现在BeanDefinition定义),借助依赖注入(Dependency Inject,DI)的能力,实现基于元数据的业务对象自动装配,可以理解成增强版的Guice,如下图所示。

Spring中的BeanFactory体系
在Spring Framework中,BeanFactory作为非常基础的接口存在,提供了Bean注册能力(包括Bean查询、类型判断、scope判断等基础能力);在继承体系上,可以分成BeanFactory实现类和ApplicationContext两条继承线路。
两者的关系如下:BeanFactory实现类属于外观模式的子系统(SubSystem)承担着外界提供Bean对象的主要责任,是功能实现的主体;ApplicationContext作为BeanFactory实现类的外观系统(Facade System),通过 BeanFactory 提供的接口结合具体的业务场景解析元数据(BeanDefinition )的解析和注册,并进行Bean对象的初步加载。
另外,ApplicationContext继承于接口 BeanFactory ,并在BeanFactory的基础上,提供AoP能力的集成、消息资源的管理、事件发布机制、以及基于特定场景(Web系统、文件系统)的能力封装等主要功能。
BeanFactory 完整的继承关系如下图所示:

BeanFactory 的主要实现类
从图中可以看出,BeanFactory 的主要实现类包括StaticListableBeanFactory、DefaultListableBeanFactory、SimpleJndiBeanFactory等三个公共类,分别面向静态场景、默认枚举配置场景和JNDI等场景,对应的实现思路也不尽相同。
StaticListableBeanFactory可以看做是基于Map<String,Object>的简单封装,不包括Bean的实例化过程,需要用户手动注册后才可以使用,不具备依赖注入能力,是BeanFactory最为简单的实现;DefaultListableBeanFactory是SpringBeanFactory的默认实现,是Spring IoC容器中最为复杂的部分,在后续的章节我们会进行分析;SimpleJndiBeanFactory则是基于JNDI场景发现对应Bean。
类 DefaultListableBeanFactory
类 DefaultListableBeanFactory 的继承关系如下图所示, 从中我们可以基本上了解到 类 DefaultListableBeanFactory 的基本功能。

DefaultListableBeanFactory的接口继承关系
从接口实现上看,类 DefaultListableBeanFactory 主要实现了以下基础的接口:
AliasRegistry:属于spring-core模块(别名能力不限制于Bean Name场景,因此定义在core模块中),提供String类型别名相关能力,对应的实现类为 SimpleAliasRegistry ,可以看做是简易版的 Map<String,String> 抽象能力,提供别名的增删改查相关能力;SingletonBeanRegistry:属于spring-beans模块,提供单例bean对象的注册和查询能力,可以看做是简易版的 Map<String,Object> ;ListableBeanFactory::属于spring-beans模块,在 BeanFactory 的基础上提供更进一步的非精确化查询能力,支持基于类型(Class和ResolvableType)的查询能力,返回的结果支持beanName 和Map<BeanName,Bean>;HierarchicalBeanFactory: 属于spring-beans模块,提供多层次的BeanFactory结构,允许多个BeanFactory之间存在继承关系。例如在 WebApplicationCpontext 的使用场景中, WebApplicationContext 依赖于基于XML配置的 XMLApplicationContext等;AutowireCapableBeanFactory:属于spring-beans模块,提供基于bean的自动装配能力,包括依赖查找(Dependence Lookup)和依赖注入(Dependence Inject)能力;-
ConfigurableBeanFactory: 属于spring-beans模块,提供 DefaultListableBeanFactory 相关数据的读取和配置接口,同时提供销毁bean 的能力。 -
ConfigurableListableBeanFactory在AutowireCapableBeanFactory提供了部分依赖的设置能力,例如,注册特定类型的依赖默认值(registerResolvableDependency(Class<?> dependencyType, Object autowiredValue)),判断某个bean依赖关系是否存在候选集合(boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor)),依赖忽略特定的接口或者类型的依赖注入(ignoreDependencyType(Class<?> type)、void ignoreDependencyInterface(Class<?> ifc))。依赖候选集的查询需要容器提供基于类型查询对应的bean的能力,因此接口ConfigurableListableBeanFactory继承了接口ListableBeanFactory,因此,在实现的所有接口中,ConfigurationListableBeanFactory的继承关系是最为复杂的,聚合了很多抽象的能力,是最接近实现类DefaultListableBeanFactory的接口,因此在spring 在对外提供BeanFactory预处理接口BeanFactoryPostProcessor的时候,对应的方法签名是void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory),而不是,这样也可以避免void postProcessBeanFactory(BeanFactory beanFactory)BeanFactoryPostProcessor#postProcessBeanFactory中传入的参数是ApplicationContext。 - 接口
BeanDefinitionRegistry提供了基于beanName的 BeanDefinition 的注册、删除和查询能力。
DefaultListableBeanFactory的类继承关系
DefaultListableBeanFactory 的类继承关系,自上到下依次对应的是
Object <- SimpleAliasRegistry <- DefaultSingletonBeanRegistry <- FactoryBeanRegistrySupport <- AbstractBeanFactory <- AbstractAutowireCapableBeanFactory <- DefaultListableBeanFactory 。
从上到下,BeanFactory 关注点变得越来越具体,逻辑也变得越来越复杂,对应的能力逐渐强大:从最初的别名注册能力,到bean对象的注册管理和FactoryBean 对象的管理,再到BeanFactory配置管理,自动装配能力的实现,最后才是BeanDefination注册管理,支持根据BeanDefination进行对象的实例化。
BeanFactory vs FactoryBean
- 两者的区别在:前者是工厂类,简单理解成 beanName和bean对象映射关系的维护者(是个容器),提供根据beanName查询bean对象的能力;后者是工厂类,描述的是Bean对象实例化的过程,用于生成特定类型的对象。 BeanFactory is a factory, FactoryBean is a bean。
FactoryBean当你向容器注册名字为 factoryBeanName 的 FactoryBean的时候,你向容器注册的是 名字为&factoryBeanName的FactoryBean的对象,,通过factoryBeanName获取到的是FactoryBean#getObject返回的对象,该对象不受Spring 容器管理,具体参考What’s a FactoryBean?。- 当创建Bean的过程中涉及到多个依赖对象的复杂配置(不是简单的属性注册),或者存在一定的复用性时,可以通过
FactoryBean简化一部分实例过程,减少无关Bean的注册。例如AbstractEntityManagerFactoryBean相关实现。
实例说明
@Configuration
public class FactoryBeanDemo {
public static final String beanName = "user";
@Bean(beanName)
public FactoryBean<User> factoryBean() {
return new FactoryBean<User>() {
public User getObject() {
User user = new User();
user.setName("mijack");
return user;
}
public Class<?> getObjectType() {
return User.class;
}
};
}
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(FactoryBeanDemo.class);
System.out.println(applicationContext.getBean(beanName) instanceof User);// true
System.out.println(applicationContext.getBean(beanName) instanceof FactoryBean); // false
System.out.println(applicationContext.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName) instanceof User);//false
System.out.println(applicationContext.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName) instanceof FactoryBean);//true
System.out.println(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, User.class));// [user]
}
}
ApplicationContext 的类家族

从 ApplicationContext 继承的接口上看,接口 ApplicationContext 继承了接口 ListableBeanFactory 、 HierarchicalBeanFactory 、 EnvironmentCapable 、MessageSource 、 ApplicationEventPublisher 、 ResourcePatternResolver,提供IoC容器能力、环境管理、事件发布机制、事件发布-响应机制、资源定位加载、i18n的能力。
从 ApplicationContext 实现的子类上看,ApplicationContext根据不用应用场景分成不同的实现类:
- 应用在普通场景,还是web场景?
- 配置文件是从XML中读取还是从Config Class中读取,或者基于Groovy脚本配置?基于XML读取的方式资源路径是感觉文件系统来读取,还是基于Classpath来读取?
图中中蓝框部分通过 ApplicationContext -> ConfigurableApplicationContext -> AbstractApplicationContext -> AbstractRefreshableApplicationContext -> AbstractRefreshableConfigApplicationContext -> AbstractRefreshableWebApplicationContext & AbstractXmlApplicationContext 的继承体系实现了相关功能,但是这样存在一定问题:不同场景下,我文件加载的方式都要重新实现一遍,例如实现XML的配置加载,需要分成文件系统和Web应用两种不同的实现类,代码复用度太低。
为了避免上述情况,进一步提高代码的利用率,Spring 提出 GenericApplicationContext类继承体系(红色框的实现体系): GenericApplicationContext 实现了接口 BeanDefinitionRegistry,将资源注册过程从核心流程中剥离出来,特定场景下数据加载解析,有子类实现,提供友好的API接口。不同于 AbstractRefreshableApplicationContext (维护内部的 BeanFactory ,支持刷新时重新创建 BeanFactory ), GenericApplicationContext 中的 beanFactory 支持从外界导入,为了避免复杂的状态管理, GenericApplicationContext 默认不支持容器的重新刷新。