1. 从Spring容器的基石说起
在Spring框架中,BeanFactory和FactoryBean这两个概念经常让开发者感到困惑。它们名称相似,却承担着完全不同的职责。要理解它们的区别,我们需要先回到Spring最基础的设计理念。
Spring框架的核心是一个轻量级的控制反转(IoC)容器,它负责管理应用中各个组件(即bean)的生命周期和依赖关系。这个容器最基础的形态就是BeanFactory接口。想象一下,BeanFactory就像是一个自动化工厂的中央控制系统,它知道:
- 工厂里有哪些生产线(bean定义)
- 如何组装产品(bean实例化)
- 产品之间的依赖关系(依赖注入)
而FactoryBean则更像是这个工厂中的一条特殊生产线,它本身也是一个产品(bean),但它的特殊之处在于它能生产其他产品。这种设计模式在Spring中被称为"工厂模式"的一种实现。
关键理解:BeanFactory是容器本身,而FactoryBean是容器中一个能生产其他bean的特殊bean。
2. BeanFactory深度解析
2.1 BeanFactory的架构定位
BeanFactory是Spring框架最基础的IoC容器接口,定义了容器最基本的行为规范。我们可以通过它的继承体系来理解它的核心职责:
java复制public interface BeanFactory {
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
// 其他基础方法...
}
这些方法揭示了BeanFactory的核心能力:
- bean的查找与获取
- bean作用域判断
- 类型检查
- 别名处理
2.2 BeanFactory的实现体系
在实际应用中,我们很少直接使用基础的BeanFactory接口,而是使用它的扩展实现。最常见的实现类包括:
- DefaultListableBeanFactory:完整的、可配置的IoC容器实现
- XmlBeanFactory(已废弃):基于XML配置的经典实现
- ApplicationContext:企业级增强容器(我们常用的ClassPathXmlApplicationContext等都是它的子类)
java复制// 基础使用示例(现代Spring应用已不推荐直接这样使用)
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
MyService service = (MyService) factory.getBean("myService");
2.3 BeanFactory的延迟加载特性
与ApplicationContext不同,基础的BeanFactory实现默认采用延迟加载策略。这意味着:
java复制// bean定义被加载,但bean实例尚未创建
BeanFactory factory = new XmlBeanFactory(resource);
// 只有调用getBean()时才会真正实例化
MyBean bean = factory.getBean(MyBean.class); // 此时才创建实例
这种特性在资源受限的环境下很有价值,但也带来了潜在的运行时错误风险(配置错误只有在实际调用时才会暴露)。
3. FactoryBean的机制剖析
3.1 FactoryBean的设计初衷
FactoryBean是一种特殊的设计模式在Spring中的实现。它的核心思想是:当一个bean的创建过程过于复杂,不适合直接用构造函数或工厂方法创建时,可以将其封装到一个专门的工厂bean中。
典型的应用场景包括:
- 集成第三方库时的适配层
- 需要根据条件动态决定实现类的场景
- 创建代理对象
- 遗留系统的适配
3.2 FactoryBean接口解析
FactoryBean接口定义非常简单但强大:
java复制public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
这三个方法分别负责:
- getObject():返回工厂创建的实际对象
- getObjectType():返回工厂创建的对象类型
- isSingleton():指示工厂是否生产单例对象
3.3 经典FactoryBean实现示例
Spring框架本身提供了许多FactoryBean的实现,例如:
- SqlSessionFactoryBean(MyBatis集成)
- ProxyFactoryBean(AOP代理创建)
- JndiObjectFactoryBean(JNDI查找)
下面是一个自定义FactoryBean的示例:
java复制public class MyConnectionFactoryBean implements FactoryBean<Connection> {
private String url;
private String username;
private String password;
// 实际的工厂方法
@Override
public Connection getObject() throws Exception {
return DriverManager.getConnection(url, username, password);
}
@Override
public Class<?> getObjectType() {
return Connection.class;
}
// 标准的setter方法
public void setUrl(String url) { this.url = url; }
public void setUsername(String username) { this.username = username; }
public void setPassword(String password) { this.password = password; }
}
在XML配置中,我们可以这样使用:
xml复制<bean id="dataSource" class="com.example.MyConnectionFactoryBean">
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="secret"/>
</bean>
当从容器中获取这个bean时:
java复制// 获取的是Connection对象,而不是FactoryBean本身
Connection conn = context.getBean("dataSource", Connection.class);
4. 核心区别对比
4.1 角色定位差异
| 特性 | BeanFactory | FactoryBean |
|---|---|---|
| 本质 | IoC容器的基础接口 | 特殊的bean类型 |
| 存在形式 | 容器本身 | 容器中的一个bean |
| 主要职责 | 管理bean的生命周期和依赖关系 | 负责创建特定的复杂对象 |
| 使用方式 | 通过getBean()获取其他bean | 实现工厂逻辑来生产目标对象 |
4.2 获取对象的区别
这是最容易混淆的地方。假设我们有一个FactoryBean实现类叫做MyFactoryBean:
java复制// 获取FactoryBean生产的对象
MyProduct product = context.getBean("myFactoryBean", MyProduct.class);
// 获取FactoryBean本身(注意'&'前缀)
MyFactoryBean factory = context.getBean("&myFactoryBean", MyFactoryBean.class);
这个设计非常巧妙:
- 默认情况下,bean名称指向工厂生产的产品
- 在bean名称前加
&才能获取工厂本身
4.3 生命周期管理的不同
BeanFactory控制着整个容器的bean生命周期,而FactoryBean本身作为一个bean,也有自己的生命周期,同时它管理的对象又有独立的状态:
-
BeanFactory的生命周期管理:
- 加载配置
- 解析bean定义
- 处理依赖关系
- 实例化bean
- 初始化bean
- 销毁bean
-
FactoryBean的生命周期:
- FactoryBean本身的实例化和初始化
- 调用getObject()创建目标对象
- 目标对象的使用
- 目标对象的销毁(如果实现了DisposableBean)
- FactoryBean本身的销毁
5. 实际应用中的经验分享
5.1 何时选择使用FactoryBean
根据我的项目经验,以下场景特别适合使用FactoryBean:
-
对象创建过程复杂:当对象的创建需要多步操作或复杂逻辑时
java复制public class ComplexServiceFactoryBean implements FactoryBean<ComplexService> { @Override public ComplexService getObject() { ComplexService service = new ComplexService(); service.setConfig(loadConfig()); service.setConnection(establishConnection()); service.validate(); return service; } // ... } -
需要条件性创建对象:根据运行时条件决定创建哪种实现
java复制public class PaymentServiceFactoryBean implements FactoryBean<PaymentService> { @Value("${payment.mode}") private String paymentMode; @Override public PaymentService getObject() { if ("credit".equals(paymentMode)) { return new CreditCardPaymentService(); } else { return new PayPalPaymentService(); } } // ... } -
需要隐藏实现细节:对外只暴露接口,但内部需要复杂初始化
java复制public class JpaRepositoryFactoryBean implements FactoryBean<Repository> { private Class<?> repositoryInterface; public void setRepositoryInterface(Class<?> repositoryInterface) { this.repositoryInterface = repositoryInterface; } @Override public Object getObject() { // 复杂的JPA代理创建逻辑 return Proxy.newProxyInstance(...); } // ... }
5.2 常见的坑与解决方案
-
循环引用问题:
FactoryBean如果依赖其他bean,而其他bean又依赖FactoryBean生产的产品,会导致循环依赖。解决方案:使用@Lazy注解延迟加载,或重构设计消除循环依赖。
-
类型匹配异常:
当FactoryBean生产的对象与声明的类型不匹配时,Spring会在运行时抛出异常。java复制// FactoryBean声明返回Connection,但实际返回了null public Connection getObject() { return null; // 这将导致后续的getBean()调用失败 } -
原型作用域的问题:
当FactoryBean本身是单例,但生产的是原型bean时,需要注意缓存问题。java复制@Scope("prototype") public class PrototypeProductFactory implements FactoryBean<Product> { private static int counter = 0; @Override public Product getObject() { counter++; Product product = new Product(); product.setSerialNumber("PROD-" + counter); return product; } // ... }即使FactoryBean是单例,每次getBean()调用getObject()都会创建新的Product实例。
5.3 性能优化建议
-
缓存策略:
对于创建成本高的对象,可以在FactoryBean中实现缓存:java复制public class ExpensiveResourceFactoryBean implements FactoryBean<Resource>, InitializingBean { private Resource resource; @Override public void afterPropertiesSet() throws Exception { // 在初始化阶段就创建好资源 this.resource = createExpensiveResource(); } @Override public Resource getObject() { return resource; } // ... } -
延迟初始化:
对于不一定会用到的资源,可以采用延迟加载:java复制public class LazyResourceFactoryBean implements FactoryBean<Resource> { private Resource resource; private final Object lock = new Object(); @Override public Resource getObject() { if (resource == null) { synchronized (lock) { if (resource == null) { resource = createResource(); } } } return resource; } // ... } -
合理使用isSingleton():
根据产品对象的特性正确实现isSingleton():java复制@Override public boolean isSingleton() { // 只有当产品对象是线程安全且无状态时才返回true return false; }
6. 高级应用场景
6.1 与Spring Boot的自动配置结合
在现代Spring Boot应用中,FactoryBean经常被用于自动配置。例如:
java复制@Configuration
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyServiceFactoryBean myService() {
return new MyServiceFactoryBean();
}
}
这种模式允许:
- 默认提供合理的配置
- 保留用户自定义的能力
- 隐藏复杂的初始化逻辑
6.2 动态代理集成
FactoryBean是Spring AOP中创建代理对象的理想选择:
java复制public class TransactionalProxyFactoryBean implements FactoryBean<Object> {
private Object target;
private PlatformTransactionManager transactionManager;
@Override
public Object getObject() {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvice(new TransactionInterceptor(transactionManager, new DefaultTransactionAttribute()));
return proxyFactory.getProxy();
}
// ...
}
6.3 多例模式的高级应用
结合Spring的作用域机制,可以实现更灵活的多例模式:
java复制public class ThreadLocalFactoryBean implements FactoryBean<Object>, DisposableBean {
private final ThreadLocal<Object> threadLocal = new ThreadLocal<>();
private Class<?> objectType;
@Override
public Object getObject() {
Object obj = threadLocal.get();
if (obj == null) {
obj = createNewInstance();
threadLocal.set(obj);
}
return obj;
}
@Override
public void destroy() {
Object obj = threadLocal.get();
if (obj != null) {
cleanup(obj);
}
}
// ...
}
这种实现可以确保每个线程获取自己的实例,并在适当的时候清理资源。
