BeanFactory 与 FactoryBean 的区别:面试深度解析
这是 Spring 面试中出镜率极高的一道题。两个名字长得像,但职责完全不同。很多人背了答案,面试官一追问就露馅。本文从定义、职责、用法、源码和常见追问几个角度,把这两个概念彻底讲清楚。
一、一句话区分
- BeanFactory :Spring 的 IoC 容器,负责管理所有 Bean 的创建、依赖注入和生命周期。
- FactoryBean :一个特殊的 Bean,用于定制某个复杂对象的创建逻辑,它本身由 BeanFactory 管理。
简单说:BeanFactory 是"工厂",FactoryBean 是"工厂生产的特殊产品"。
二、BeanFactory 详解
2.1 定义
BeanFactory 是 Spring IoC 容器的顶层接口,定义了容器的最基本行为:
java
public interface BeanFactory {
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name);
boolean isPrototype(String name);
Class<?> getType(String name);
// ...
}
它提供了 getBean()、containsBean()、isSingleton() 等核心方法,是 Spring 管理 Bean 的基础设施。
2.2 常见实现
DefaultListableBeanFactory:最核心的实现,功能最完整。XmlBeanFactory:基于 XML 配置的容器(已过时)。AnnotationConfigApplicationContext:基于注解配置的容器,是ApplicationContext的实现。
2.3 职责
- 读取配置(XML、注解、Java 配置)生成 BeanDefinition。
- 实例化 Bean。
- 依赖注入。
- 管理 Bean 的作用域和生命周期。
- 提供
getBean()供外部获取 Bean。
2.4 使用示例
java
BeanFactory factory = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = factory.getBean(UserService.class);
三、FactoryBean 详解
3.1 定义
FactoryBean 是一个接口,用于创建复杂的 Bean。它本身也是一个 Bean,由 Spring 容器管理,但通过 getBean() 获取它时,返回的是 getObject() 方法创建的对象,而不是 FactoryBean 本身。
java
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
}
3.2 三个方法
getObject():返回真正需要的对象。getObjectType():返回对象的类型。isSingleton():返回对象是否为单例。
3.3 使用示例
假设我们需要创建一个复杂的 Connection 对象,创建过程很繁琐,可以用 FactoryBean 封装:
java
public class ConnectionFactoryBean implements FactoryBean<Connection> {
@Override
public Connection getObject() throws Exception {
// 复杂的创建逻辑
Connection conn = DriverManager.getConnection("jdbc:mysql://...");
// 其他初始化
return conn;
}
@Override
public Class<?> getObjectType() {
return Connection.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
在 XML 中注册:
xml
<bean id="connection" class="com.example.ConnectionFactoryBean"/>
获取时:
java
// 获取的是 getObject() 返回的 Connection 对象
Connection conn = context.getBean("connection", Connection.class);
// 获取 FactoryBean 本身,需要在 beanName 前加 &
ConnectionFactoryBean factoryBean =
context.getBean("&connection", ConnectionFactoryBean.class);
3.4 典型应用
- MyBatis 的
SqlSessionFactoryBean:用于创建SqlSessionFactory。 - Spring 的
ProxyFactoryBean:用于创建 AOP 代理对象。 JndiObjectFactoryBean:用于从 JNDI 获取对象。
四、核心区别对比
| 对比维度 | BeanFactory | FactoryBean |
|---|---|---|
| 本质 | IoC 容器顶层接口 | 一个特殊的 Bean |
| 职责 | 管理所有 Bean 的创建、依赖注入、生命周期 | 定制某个复杂对象的创建逻辑 |
| 获取方式 | getBean("beanName") 获取容器中的 Bean |
getBean("beanName") 返回 getObject() 的结果;getBean("&beanName") 返回 FactoryBean 本身 |
| 是否由容器管理 | 本身就是容器 | 是一个 Bean,由容器管理 |
| 实现方式 | 有多个实现类,如 DefaultListableBeanFactory |
由开发者实现接口,注册为 Bean |
| 使用场景 | 所有 Spring 应用的基础设施 | 创建复杂对象、集成第三方框架、创建代理对象 |
| 数量 | 一个应用通常一个容器 | 可以有多个 FactoryBean |
| 生命周期 | 容器级 | Bean 级,受容器管理 |
五、源码层面:getBean 如何处理 FactoryBean
当调用 getBean("connection") 时,Spring 会检查这个 Bean 是否实现了 FactoryBean 接口:
java
// AbstractBeanFactory.getObjectForBeanInstance 简化逻辑
if (beanInstance instanceof FactoryBean) {
if (!(name.startsWith("&"))) {
// 返回 getObject() 创建的对象
return ((FactoryBean<?>) beanInstance).getObject();
} else {
// 返回 FactoryBean 本身
return beanInstance;
}
}
如果 beanName 以 & 开头,返回 FactoryBean 本身;否则返回 getObject() 的结果。这就是 getBean("&connection") 能拿到 FactoryBean 的原因。
六、常见面试追问
1. BeanFactory 和 ApplicationContext 的关系?
ApplicationContext 是 BeanFactory 的子接口,继承了 BeanFactory 的所有能力,并增加了:
- 国际化(MessageSource)
- 事件发布(ApplicationEventPublisher)
- 资源加载(ResourcePatternResolver)
- 自动装配
BeanPostProcessor、BeanFactoryPostProcessor - 启动时预加载所有单例 Bean(
BeanFactory默认懒加载)
ApplicationContext 是面向应用开发的容器,BeanFactory 是底层基础设施。
2. 如何获取 FactoryBean 本身?
在 beanName 前加 &:
java
ConnectionFactoryBean factoryBean = context.getBean("&connection", ConnectionFactoryBean.class);
3. FactoryBean 和 @Bean 的区别?
@Bean是方法级别的注解,在@Configuration类中定义 Bean。FactoryBean是类级别的接口,用于创建复杂对象,通常用于框架集成。@Bean更简洁,FactoryBean更灵活,适合复杂的创建逻辑。
4. FactoryBean 的 isSingleton() 返回 true 时,getObject() 会被调用几次?
如果 isSingleton() 返回 true,Spring 会缓存 getObject() 的结果,多次获取返回同一个对象。如果返回 false,每次 getBean() 都会调用 getObject() 创建新对象。
5. 为什么 MyBatis 用 FactoryBean?
MyBatis 的 SqlSessionFactory 创建过程复杂,需要配置数据源、MyBatis 配置、Mapper 扫描等。用 FactoryBean 把这些逻辑封装起来,Spring 只需要管理 SqlSessionFactoryBean,通过它获取 SqlSessionFactory。
七、总结
| 维度 | BeanFactory | FactoryBean |
|---|---|---|
| 是什么 | IoC 容器 | 创建复杂对象的特殊 Bean |
| 核心方法 | getBean() |
getObject() |
| 获取方式 | getBean("name") |
getBean("name") 返回产品,getBean("&name") 返回自身 |
| 谁管理 | 本身是容器 | 由 BeanFactory 管理 |
| 典型实现 | DefaultListableBeanFactory |
SqlSessionFactoryBean、ProxyFactoryBean |
| 面试频率 | 高 | 高 |
记住一句口诀:BeanFactory 是容器,FactoryBean 是产品。容器管所有,产品管一个。
理解这两者的区别,不仅能应对面试,更能帮你理解 Spring 框架的设计思想------容器与扩展点分离,基础设施与业务逻辑解耦。