BeanFactory API
BeanFactory API 为 Spring 的 IoC 功能提供了底层基础。它的特定契约主要用于与 Spring 的其他部分以及相关的第三方框架集成,其 DefaultListableBeanFactory 实现是更高级别 GenericApplicationContext 容器中的关键委托。
BeanFactory 及相关接口(如 BeanFactoryAware、InitializingBean、DisposableBean)是其他框架组件的重要集成点。由于不需要任何注解甚至反射,它们允许容器与其组件之间进行非常高效的交互。应用级别的 Bean 也可以使用相同的回调接口,但通常更倾向于使用声明式依赖注入,无论是通过注解还是通过编程式配置。
请注意,核心 BeanFactory API 级别及其 DefaultListableBeanFactory 实现并不对配置格式或要使用的任何组件注解做假设。所有这些特性都通过扩展(如 XmlBeanDefinitionReader 和 AutowiredAnnotationBeanPostProcessor)进入,并操作共享的 BeanDefinition 对象作为核心元数据表示。这就是使 Spring 容器如此灵活和可扩展的本质所在。
BeanFactory 还是 ApplicationContext?
本节解释了 BeanFactory 和 ApplicationContext 容器级别之间的区别,以及对引导(Bootstrapping)的影响。
除非你有充分的理由不这样做,否则你应该使用 ApplicationContext。GenericApplicationContext 及其子类 AnnotationConfigApplicationContext 是自定义引导的常用实现。这些是所有常见用途中进入 Spring 核心容器的主要入口点:加载配置文件、触发类路径扫描、编程式注册 Bean 定义和注解类,以及(从 5.0 开始)注册函数式 Bean 定义。
由于 ApplicationContext 包含 BeanFactory 的所有功能,因此通常建议使用它而不是普通的 BeanFactory,除非在需要完全控制 Bean 处理的场景中。在 ApplicationContext(如 GenericApplicationContext 实现)内,几种类型的 Bean 是按约定检测的(即通过 Bean 名称或 Bean 类型——特别是后处理器),而普通的 DefaultListableBeanFactory 对任何特殊 Bean 都是不可知的。
对于许多扩展的容器功能(如注解处理和 AOP 代理),BeanPostProcessor 扩展点是必不可少的。如果你只使用普通的 DefaultListableBeanFactory,默认情况下这些后处理器不会被检测并激活。这种情况可能会令人困惑,因为你的 Bean 配置实际上没有任何问题。相反,在这样的场景下,容器需要通过额外的设置来完全引导。
下表列出了 BeanFactory 和 ApplicationContext 接口及实现提供的功能。
| 功能 | BeanFactory | ApplicationContext |
|---|---|---|
| Bean 实例化/装配 | 是 | 是 |
| 集成的生命周期管理 | 否 | 是 |
自动 BeanPostProcessor 注册 | 否 | 是 |
自动 BeanFactoryPostProcessor 注册 | 否 | 是 |
便捷的 MessageSource 访问(用于国际化) | 否 | 是 |
内置 ApplicationEvent 发布机制 | 否 | 是 |
要显式地向 DefaultListableBeanFactory 注册 Bean 后处理器,你需要以编程方式调用 addBeanPostProcessor,如下例所示:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// 用 Bean 定义填充工厂
// 现在注册任何需要的 BeanPostProcessor 实例
factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
factory.addBeanPostProcessor(new MyBeanPostProcessor());
// 现在开始使用工厂val factory = DefaultListableBeanFactory()
// 用 Bean 定义填充工厂
// 现在注册任何需要的 BeanPostProcessor 实例
factory.addBeanPostProcessor(AutowiredAnnotationBeanPostProcessor())
factory.addBeanPostProcessor(MyBeanPostProcessor())
// 现在开始使用工厂要将 BeanFactoryPostProcessor 用于普通的 DefaultListableBeanFactory,你需要调用其 postProcessBeanFactory 方法,如下例所示:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(new FileSystemResource("beans.xml"));
// 从属性文件引入一些属性值
PropertySourcesPlaceholderConfigurer cfg = new PropertySourcesPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
// 现在实际执行替换操作
cfg.postProcessBeanFactory(factory);val factory = DefaultListableBeanFactory()
val reader = XmlBeanDefinitionReader(factory)
reader.loadBeanDefinitions(FileSystemResource("beans.xml"))
// 从属性文件引入一些属性值
val cfg = PropertySourcesPlaceholderConfigurer()
cfg.setLocation(FileSystemResource("jdbc.properties"))
// 现在实际执行替换操作
cfg.postProcessBeanFactory(factory)在这两种情况下,显式注册步骤都很不方便,这就是为什么在基于 Spring 的应用程序中,各种 ApplicationContext 变体优于普通的 DefaultListableBeanFactory,特别是在典型的企业级设置中依赖 BeanFactoryPostProcessor 和 BeanPostProcessor 实例来获得扩展容器功能时。
TIP
AnnotationConfigApplicationContext 已经注册了所有常见的注解后处理器,并且可能通过配置注解(如 @EnableTransactionManagement)在后台引入其他处理器。在 Spring 基于注解的配置模型抽象级别上,Bean 后处理器的概念仅成为容器内部的细节。
补充教学
1. 继承与组合的关系
在源码层面,ApplicationContext 接口 继承 了 BeanFactory。这意味着所有的 ApplicationContext 都是一个 BeanFactory。 然而,在常用的实现类(如 GenericApplicationContext)中,它们内部 组合(持有) 了一个 DefaultListableBeanFactory 实例。所有的 Bean 管理任务最终都会委托(Delegate)给这个内部的 BeanFactory 来完成。
2. 延迟加载 vs 预加载
- BeanFactory:默认采用 延迟加载(Lazy Loading) 策略。只有当你调用
getBean()时,它才会去实例化那个 Bean。这非常适合移动端或内存极其受限的环境。 - ApplicationContext:默认会 预加载(Eager Pre-instantiation) 所有的单例(Singleton)Bean。这样做的好处是可以在启动阶段发现配置错误(Fail-fast),并提高运行时的性能,因为 Bean 已经在内存中准备好了。
3. DefaultListableBeanFactory 的地位
即使你从不直接使用它,你也应该知道 DefaultListableBeanFactory。它是 Spring 核心容器的“发动机”。
- 它实现了所有的 Bean 注册、查找、装配逻辑。
ApplicationContext只是给这个“发动机”套上了一个名为“框架功能”的外壳(支持国际化、事件、资源加载、自动发现后处理器等)。
4. 何时该直接使用 BeanFactory?
在绝大多数商业项目中,永远不要 直接使用 BeanFactory。 只有在以下极端场景下才考虑:
- 极度内存敏感:比如嵌入式设备,需要快速启动且内存占用最小。
- 中间件/框架开发:你正在编写一个不希望依赖太多 Spring 高级功能的底层库。
- 极简单元测试:只需要验证某些原始的 Bean 装配逻辑,不涉及 AOP 或注解处理。
如果你发现 @Autowired 不起作用,或者 ${placeholder} 没被解析,通常是因为你正在使用原始的 BeanFactory 而没有手动注册对应的 Processor。