在Spring框架中,有两个名字极其相似的关键接口:BeanFactory
和 FactoryBean
。它们就像一对双胞胎兄弟,名字相似却各司其职,常常让开发者困惑不已。今天我们就来彻底厘清它们的关系、区别以及如何在实际项目中正确使用。
一、BeanFactory:Spring 的“心脏”,IoC 容器的基础
定义:
BeanFactory
是 Spring IoC 容器的核心接口,负责管理 Bean 的生命周期(创建、配置、组装)。它是 Spring 的“心脏”,所有其他容器(如 ApplicationContext
)都是它的扩展。
核心功能:
public interface BeanFactory {
Object getBean(String name) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
// ... 其他方法
}
特点:
- 懒加载:默认在第一次请求 Bean 时才实例化。
- 轻量级:提供基础依赖注入功能,适合资源受限环境。
- 所有容器的基础:
ApplicationContext
扩展了它,添加了更多企业级功能(AOP、事件等)。
二、FactoryBean:一个能“生产”其他 Bean 的特殊 Bean
定义:
FactoryBean
是一个工厂 Bean,用于创建其他 Bean 的实例。当你从容器请求一个 FactoryBean
时,实际得到的是它生产的对象(getObject()
的返回值),而不是 FactoryBean
本身。
核心方法:
public interface FactoryBean<T> {
T getObject() throws Exception; // 返回生产的Bean实例
Class<?> getObjectType(); // 返回生产的Bean类型
boolean isSingleton(); // 生产的Bean是否为单例
}
为什么需要FactoryBean?
Spring容器默认通过反射创建Bean,但当遇到以下场景时,反射机制显得力不从心:
- 复杂初始化过程:需要多步初始化逻辑的对象(如连接池)
- 第三方库集成:无法直接通过构造器创建的组件(如MyBatis的SqlSessionFactory)
- 动态代理:需要运行时生成代理对象的场景
- 条件创建:根据配置动态决定创建哪种实现类
- 资源封装:需要统一管理资源初始化和销毁
FactoryBean通过将复杂对象的创建逻辑封装在工厂中,扩展了Spring容器的对象创建能力。
三、BeanFactory vs FactoryBean:关键区别与联系
特性 | BeanFactory | FactoryBean |
---|---|---|
角色 | IoC 容器的基础接口 | 一个能生产其他 Bean 的特殊 Bean |
作用 | 管理 Bean 的整个生命周期 | 封装复杂对象的创建逻辑 |
获取自身对象 | 直接通过 getBean() | 需在 Bean 名前加 & (如 &myFactoryBean ) |
是否接口 | 是,Spring 核心接口 | 是,Spring 扩展接口 |
关系 | FactoryBean 本身由 BeanFactory 管理 | FactoryBean 生产的对象由 BeanFactory 管理 |
✅ 联系总结:
FactoryBean
本身作为一个 Bean,被注册在BeanFactory
中。BeanFactory
通过调用FactoryBean.getObject()
获取目标对象。- 二者协作扩展了 Spring 容器的能力。
四、什么时候应该使用FactoryBean?
在以下场景中,FactoryBean是理想选择:
-
创建过程复杂的对象
当对象初始化需要多步操作(如连接池配置、线程池设置)时:public class ThreadPoolFactoryBean implements FactoryBean<ExecutorService> { @Override public ExecutorService getObject() { return Executors.newFixedThreadPool(10); } // ... }
-
集成第三方框架
适配无法直接通过Spring管理的组件:<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> </bean>
-
需要动态决策的实现
根据运行时条件创建不同实现类:public class PaymentServiceFactoryBean implements FactoryBean<PaymentService> { @Override public PaymentService getObject() { return isProduction() ? new AliPayService() : new MockPayService(); } // ... }
-
创建代理对象
需要AOP增强的特殊Bean:public class ServiceProxyFactoryBean implements FactoryBean<Object> { private Object target; @Override public Object getObject() { return Proxy.newProxyInstance(/*...*/); } // ... }
-
资源敏感型对象
需要精确控制初始化和销毁的资源:public class FileResourceFactoryBean implements FactoryBean<Resource> { @Override public Resource getObject() { return new FileSystemResource("config.properties"); } @Override public void destroy() { // 显式释放文件句柄等资源 } }
五、FactoryBean 最佳实践:项目中这样用才高效
1️⃣ 封装复杂初始化逻辑(实战代码)
public class ConnectionPoolFactoryBean implements FactoryBean<DataSource> {
private String url;
private String username;
private String password;
@Override
public DataSource getObject() throws Exception {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(username);
config.setPassword(password);
config.setMaximumPoolSize(20);
config.setConnectionTimeout(30000);
return new HikariDataSource(config);
}
@Override
public Class<?> getObjectType() {
return DataSource.class;
}
@Override
public boolean isSingleton() {
return true; // 连接池通常为单例
}
}
配置使用:
<bean id="dataSource" class="com.example.ConnectionPoolFactoryBean">
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="secret"/>
</bean>
2️⃣ 集成 MyBatis(经典案例)
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath*:mappers/*.xml"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
3️⃣ 避免的坑:获取 FactoryBean 本身
// 获取 FactoryBean 生产的对象(DataSource)
DataSource ds = context.getBean("dataSource");
// 获取 FactoryBean 本身(加 & 前缀)
FactoryBean<?> factoryBean = context.getBean("&dataSource");
✅ 最佳实践原则:
- 命名清晰:如
xxxFactoryBean
表明身份 - 保持无状态:
getObject()
应幂等(多次调用返回相同实例) - 正确实现
getObjectType
:避免 Spring 类型检查出错 - 优先使用单例:除非有明确需求,否则
isSingleton()
返回true
- 避免循环依赖:FactoryBean 的初始化可能依赖其他 Bean
- 资源清理:实现
DisposableBean
释放资源 - 避免过度使用:简单对象直接使用
@Bean
更合适
六、总结:理解协作,灵活运用
- BeanFactory:Spring 的容器基石,管理所有 Bean 的“大管家”。
- FactoryBean:特殊工人,负责生产复杂对象,扩展容器的对象创建能力。
关键决策点:
当遇到以下情况时,请选择 FactoryBean:
- 对象创建需要复杂初始化逻辑
- 需要集成非Spring友好的第三方库
- 要求精确控制资源生命周期
- 需要运行时动态决定实现类
- 创建过程涉及异常处理或重试机制
在大型项目中,合理使用 FactoryBean
能:
- 大幅简化复杂组件的配置(如中间件集成)
- 集中管理初始化逻辑(如线程池、连接池)
- 提升配置可读性和可维护性
- 统一资源管理,避免内存泄漏
记住关键点:当你需要创建的对象构造过程复杂时,就该考虑 FactoryBean
了!而 BeanFactory
始终是背后默默支撑整个 Spring 宇宙的基石。