BeanFactory、ApplicationContext 和 FactoryBean 有什么区别?
简化版
BeanFactory 是 Spring IoC 容器的基础接口,ApplicationContext 在它之上增加事件、国际化、资源加载、环境与自动注册常用后处理器等企业级能力。FactoryBean<T> 不是容器,而是一个特殊 Bean:容器通过它的 getObject() 暴露复杂产品对象,使用 &beanName 才能取得工厂本身。
详细版
ApplicationContext 继承 BeanFactory,普通应用应优先使用它。BeanFactory 描述获取 Bean、查询类型和作用域等核心能力;ApplicationContext 还整合 ApplicationEventPublisher、MessageSource、ResourcePatternResolver、EnvironmentCapable 等接口,并负责更完整的容器启动流程。
FactoryBean 用于把复杂对象的创建逻辑接入容器,例如某些代理、客户端或框架基础设施。Bean 名称为 client 时,getBean("client") 得到其产品,getBean("&client") 得到 FactoryBean;它和“创建 Bean 的 BeanFactory”不是同一个层次的概念。
完整版教学
一、BeanFactory 是容器能力的根
BeanFactory 最核心的职责是根据名称或类型提供 Bean,并管理依赖关系、作用域和生命周期。它定义的是容器对外的基本契约,具体注册 BeanDefinition、创建单例和处理依赖的工作由其实现完成。
UserService service = beanFactory.getBean(UserService.class);
boolean exists = beanFactory.containsBean("userService");
日常业务代码不应到处主动调用 getBean(),否则会从依赖注入退回服务定位器模式;这里的 API 更多用于框架集成和理解容器边界。
二、ApplicationContext 增加了什么
ApplicationContext 是更完整的应用容器,除 BeanFactory 能力外,还提供:
- 发布和监听应用事件;
- 国际化消息解析;
- 统一的资源与通配路径加载;
- Environment、Profile 和属性源访问;
- 更方便的注解扫描及后处理器集成。
常见实现包括 AnnotationConfigApplicationContext,Web MVC 中则使用 WebApplicationContext。现代 Spring 应用通常不会直接实例化一个裸 BeanFactory。
三、不要死背“懒加载与立即加载”
经典面试答案常说 BeanFactory 懒加载、ApplicationContext 启动时立即创建 Bean,这不够准确。是否预实例化单例取决于具体容器实现和刷新流程:ApplicationContext 默认在 refresh() 后预实例化非 lazy 的 singleton,但标记 @Lazy 的 Bean 仍可延迟创建;BeanFactory 也可以显式调用预实例化逻辑。
真正稳定的区别是接口能力和启动管理方式,而不是把二者简单等同于“懒”与“饿”。
四、FactoryBean 如何生产对象
class ClientFactoryBean implements FactoryBean<Client> {
@Override
public Client getObject() {
return new Client("https://api.example.com");
}
@Override
public Class<?> getObjectType() {
return Client.class;
}
}
FactoryBean 自身仍由容器管理,但正常按 Bean 名获取时,容器返回 getObject() 的结果。getObjectType() 帮助容器在产品尚未创建时进行类型匹配;isSingleton() 用来说明 FactoryBean 返回的产品是否按单例语义管理,默认值是 true。
五、FactoryBean 与 @Bean 工厂方法
两者都能封装对象创建过程,但使用场景不同。@Bean 方法更直观,适合应用配置;FactoryBean 是容器级扩展协议,适合框架需要以统一方式暴露复杂产品对象时使用。
还要区分 FactoryBean 的产品单例与 FactoryBean 自身的作用域。工厂 Bean 可以是 singleton,但它声明返回的产品不一定是 singleton;判断时要同时看 BeanDefinition 作用域和 isSingleton() 语义。
六、用启动与取对象流程区分三者
ApplicationContext.refresh() 会准备 BeanFactory、注册后处理器、初始化事件与消息资源,并在末段预实例化非懒加载 singleton;这解释了为什么配置错误经常在应用启动时暴露。一个容器里假设有 200 个非懒 singleton 和 20 个 @Lazy singleton,刷新阶段通常会主动创建前 200 个;后 20 个若未被非懒 Bean 的依赖链提前拉起,则在首次需要时创建;这只是默认行为,不是 BeanFactory 与 ApplicationContext 的接口定义本身。
ApplicationContext.refresh()
│
├─ 配置并驱动内部 BeanFactory
├─ 注册 BeanPostProcessor / 事件等基础设施
└─ 预实例化非 lazy singleton
getBean("client") ──> FactoryBean.getObject() ──> Client 产品
getBean("&client") ─────────────────────────────> FactoryBean 本身
| 名称 | 它是什么 | 面试时最关键的边界 |
|---|---|---|
BeanFactory | IoC 容器基础契约 | 定义 Bean 获取、类型、作用域等核心能力 |
ApplicationContext | 完整应用上下文 | 继承 BeanFactory,并驱动完整启动和企业级能力 |
FactoryBean<T> | 容器管理的特殊 Bean | 普通名字暴露产品,& 前缀取得工厂 |
易错点:FactoryBean 的
isSingleton()描述的是产品是否可按单例语义缓存,不等于 FactoryBean 自身的 BeanDefinition 一定是 singleton。
七、常见误区与追问
- 误区:BeanFactory 一定懒加载,ApplicationContext 一定饿加载。 预实例化取决于具体实现、刷新流程和 lazy 配置,稳定区别是接口能力与上下文启动职责。
- 误区:FactoryBean 就是 BeanFactory 的实现。 前者是生产某种产品对象的扩展协议,后者是管理 Bean 的容器基础接口,层次完全不同。
- 误区:FactoryBean 的 isSingleton 返回 false 就必然每次得到全新独立对象。 它只否定单例缓存语义;若要明确原型语义,还要结合实现及
SmartFactoryBean等契约判断。 - 追问:为什么 getObjectType 很重要? 容器可能在产品尚未创建时做按类型查询和自动装配;返回准确类型能避免该 FactoryBean 被类型匹配忽略。
- 追问:FactoryBean 生产对象的销毁由谁负责? 容器主要管理 FactoryBean 自身;工厂若持有需关闭的产品资源,通常应在自身销毁回调中负责委托清理。
- 追问:业务配置优先用 @Bean 还是 FactoryBean? 普通应用对象优先选择直观的
@Bean;需要接入容器级产品语义、延迟类型判断或框架基础设施时再考虑 FactoryBean。
八、加强记忆
BeanFactory 是 IoC 容器的基础契约,ApplicationContext 是日常应用使用的完整容器;FactoryBean 则是容器管理的特殊工厂对象。普通名字取产品,&名字 取工厂本身;不要再用“一个懒加载、一个立即加载”概括全部区别。