引言
記得很久以前經常被問到這樣一個面試題"FactoryBean 和BeanFactory它們有啥區別"。在 Spring 框架中,BeanFactory 和 FactoryBean 是兩個核心概念,雖然名稱相似,但它們的角色和功能完全不同。
1. 定義與角色
| 維度 | BeanFactory | FactoryBean |
|---|---|---|
| 角色 | Spring 的 IoC 容器核心接口,負責管理所有 Bean 的生命周期(創建、配置、依賴注入)。 | 一個 特殊 Bean 接口,用于動態創建復雜對象(如代理對象、連接池、動態代理等)。 |
| 功能 | 提供容器基礎能力(如 getBean()、containsBean() 等)。 |
通過 getObject() 方法返回實際需要的 Bean 實例。 |
| 接口方法 | getBean()、containsBean()、isSingleton() 等。 |
getObject()、getObjectType()、isSingleton()。 |
| 典型實現 | DefaultListableBeanFactory、ApplicationContext 等。 |
SqlSessionFactoryBean(MyBatis)、ProxyFactoryBean(AOP)等。 |
2. 核心區別
| 特性 | BeanFactory | FactoryBean |
|---|---|---|
| 獲取對象的方式 | 直接返回容器中注冊的 Bean 實例(如 getBean("beanName"))。 |
默認返回 getObject() 的結果(如 getBean("factoryBeanName"))。 |
| 訪問自身的方式 | 直接通過 getBean("beanName") 獲取。 |
需通過 &beanName 前綴獲取(如 getBean("&factoryBeanName"))。 |
| 是否單例 | 容器默認管理 Bean 的作用域(如單例、原型)。 | 通過 isSingleton() 方法定義創建對象的作用域。 |
| 使用場景 | 管理所有 Bean 的基礎設施(如依賴注入、生命周期管理)。 | 封裝復雜對象的創建邏輯(如動態代理、數據庫連接池)。 |
3. 使用場景與示例
(1) BeanFactory 的使用
- 作用:作為 Spring 容器的根接口,負責管理所有 Bean 的生命周期。
- 示例:
// 通過 BeanFactory 獲取 Bean BeanFactory factory = new ClassPathXmlApplicationContext("beans.xml"); UserService userService = factory.getBean("userService", UserService.class);
(2) FactoryBean 的使用
- 作用:通過自定義
getObject()方法創建復雜對象。 - 示例:
// 定義 FactoryBean public class MyConnectionFactory implements FactoryBean<Connection> { @Override public Connection getObject() throws Exception { // 返回數據庫連接 return DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb"); } @Override public Class<?> getObjectType() { return Connection.class; } @Override public boolean isSingleton() { return true; // 是否為單例 } } // 配置到 Spring 容器 @Configuration public class AppConfig { @Bean public FactoryBean<Connection> connectionFactory() { return new MyConnectionFactory(); } } // 使用 @Autowired private Connection connection; // 實際注入的是 getObject() 返回的 Connection
在 Spring 框架中,FactoryBean 被廣泛用于集成第三方中間件或框架,通過封裝復雜對象的創建邏輯,簡化配置并提高靈活性。以下是幾個常見中間件使用 FactoryBean 的示例:
1. MyBatis 的 SqlSessionFactoryBean
作用:
創建 MyBatis 的 SqlSessionFactory 實例,集成數據庫配置、映射文件掃描等邏輯。
代碼示例:
<!-- Spring 配置文件中定義 SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
實現原理:
SqlSessionFactoryBean實現了FactoryBean<SqlSessionFactory>。getObject()方法內部調用 MyBatis 的SqlSessionFactoryBuilder創建SqlSessionFactory。- 支持延遲加載和復雜配置(如多數據源、事務管理器)。
2. OpenFeign 的 FeignClientFactoryBean
作用:
動態創建 Feign 客戶端(RESTful API 調用代理對象)。
代碼示例:
@Configuration
public class FeignConfig {
@Bean
public FactoryBean<MyServiceClient> myServiceClient() {
FeignClientFactoryBean factory = new FeignClientFactoryBean();
factory.setUrl("http://example.com/api");
factory.setType(MyServiceClient.class);
return factory;
}
}
實現原理:
FeignClientFactoryBean封裝了 Feign 的Target和Encoder/Decoder配置。getObject()返回動態代理的 Feign 客戶端實例。- 支持自定義攔截器、重試策略等。
3. Redis 的 RedisConnectionFactoryBean
作用:
創建 Redis 連接池(如 JedisConnectionFactory 或 LettuceConnectionFactory)。
代碼示例:
<!-- Spring 配置文件中定義 RedisConnectionFactoryBean -->
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="localhost"/>
<property name="port" value="6379"/>
</bean>
實現原理:
JedisConnectionFactory本身實現了FactoryBean<RedisConnection>。getObject()返回RedisConnection實例(如JedisConnection)。- 支持連接池配置(如最大連接數、超時時間)。
4. RocketMQ 的 RocketMQTemplate
作用:
封裝 RocketMQ 生產者和消費者的創建邏輯。
代碼示例:
@Configuration
public class RocketMQConfig {
@Bean
public RocketMQTemplate rocketMQTemplate() {
RocketMQTemplate template = new RocketMQTemplate();
template.setProducer(new DefaultMQProducer("my-producer-group"));
template.setConsumer(new DefaultMQPushConsumer("my-consumer-group"));
return template;
}
}
實現原理:
RocketMQTemplate通過FactoryBean模式初始化生產者和消費者。getObject()返回配置好的RocketMQTemplate實例。- 支持消息發送、監聽器注冊等操作。
5. Quartz 的 SchedulerFactoryBean
作用:
創建 Quartz 調度器(Scheduler),集成任務調度邏輯。
代碼示例:
<!-- Spring 配置文件中定義 SchedulerFactoryBean -->
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="myCronTrigger"/>
</list>
</property>
</bean>
實現原理:
SchedulerFactoryBean封裝了 Quartz 的SchedulerFactory配置。getObject()返回Scheduler實例。- 支持動態注冊任務和觸發器。
6. Dubbo 的 ServiceBean
作用:
發布 Dubbo 服務,封裝服務暴露和注冊邏輯。
代碼示例:
<!-- Spring 配置文件中定義 Dubbo ServiceBean -->
<bean id="dubboService" class="com.alibaba.dubbo.config.ServiceBean">
<property name="interface" value="com.example.MyService"/>
<property name="ref" ref="myServiceImpl"/>
</bean>
實現原理:
ServiceBean實現了FactoryBean<Exporter>。getObject()返回服務導出器(Exporter),完成服務注冊和暴露。- 支持負載均衡、容錯策略等 Dubbo 特性。
7. Kafka 的 KafkaTemplate
作用:
封裝 Kafka 生產者和消費者的創建邏輯。
代碼示例:
@Configuration
public class KafkaConfig {
@Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(new ProducerFactory<>());
}
}
實現原理:
KafkaTemplate通過FactoryBean模式初始化生產者工廠。getObject()返回配置好的KafkaTemplate實例。- 支持消息發送、消費者監聽等操作。
總結對比
| 中間件 | FactoryBean 類型 | 作用領域 | 核心方法作用 |
|---|---|---|---|
| MyBatis | SqlSessionFactoryBean |
數據庫訪問 | 創建 SqlSessionFactory |
| OpenFeign | FeignClientFactoryBean |
RESTful 客戶端 | 創建 Feign 動態代理 |
| Redis | JedisConnectionFactory |
緩存/鍵值存儲 | 創建 Redis 連接池 |
| RocketMQ | RocketMQTemplate |
消息隊列 | 封裝生產者/消費者邏輯 |
| Quartz | SchedulerFactoryBean |
定時任務調度 | 創建調度器并注冊任務 |
| Dubbo | ServiceBean |
微服務 RPC | 發布服務并注冊到注冊中心 |
| Kafka | KafkaTemplate |
消息隊列 | 封裝生產者/消費者邏輯 |
- 解耦配置與邏輯:通過
FactoryBean將復雜初始化邏輯封裝,Spring 容器只需管理 Bean 的聲明。 - 支持動態創建:可根據運行時條件(如環境變量、配置參數)動態生成不同對象。
- 統一資源管理:集中管理中間件的連接池、配置參數,便于維護和擴展。
4. 獲取 FactoryBean 本身
- 默認行為:
getBean("factoryBeanName")返回的是FactoryBean.getObject()的結果。 - 獲取 FactoryBean 實例本身:需在 Bean 名稱前加
&前綴。// 獲取 FactoryBean 創建的 Bean Connection connection = context.getBean("connectionFactory", Connection.class); // 獲取 FactoryBean 實例本身 MyConnectionFactory factoryBean = (MyConnectionFactory) context.getBean("&connectionFactory");
5. 常見問題與解決方案
問題1:混淆 BeanFactory 和 FactoryBean 的功能
- 解決方案:明確
BeanFactory是容器,FactoryBean是創建 Bean 的工具。
問題2:期望獲取 FactoryBean 實例卻得到其創建的 Bean
- 示例:
// 錯誤:獲取的是 Encryptor 實例,而非 FactoryBean Encryptor encryptor = factory.getBean("encryptor"); // 正確:添加 "&" 前綴獲取 FactoryBean FactoryBean factoryBean = factory.getBean("&encryptor");
問題3:未正確實現 getObjectType() 導致類型檢查失敗
- 修復:確保
getObjectType()返回準確的類型信息。
6. 高級應用場景
(1) 動態代理生成
public class ServiceProxyFactoryBean implements FactoryBean<MyService> {
@Override
public MyService getObject() {
return (MyService) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class[]{MyService.class},
(proxy, method, args) -> {
System.out.println("Before method: " + method.getName());
return method.invoke(new MyServiceImpl(), args);
});
}
}
(2) 延遲初始化
public class LazyInitFactoryBean implements FactoryBean<ExpensiveBean> {
private ExpensiveBean instance;
@Override
public ExpensiveBean getObject() {
if (instance == null) {
instance = new ExpensiveBean(); // 延遲初始化
}
return instance;
}
}
7. 總結
| 維度 | BeanFactory | FactoryBean |
|---|---|---|
| 本質 | Spring 容器的根接口,管理所有 Bean 的生命周期。 | 一個特殊 Bean,用于封裝復雜對象的創建邏輯。 |
| 獲取方式 | getBean("beanName") 返回容器中的 Bean 實例。 |
getBean("factoryBeanName") 返回 getObject() 的結果,getBean("&factoryBeanName") 返回 FactoryBean 本身。 |
| 典型用途 | Spring 容器的基礎功能(如依賴注入、生命周期管理)。 | 創建動態代理、連接池、復雜對象等。 |
- BeanFactory 是 Spring 容器本身,負責管理所有 Bean。
- FactoryBean 是 容器中的一個 Bean,負責 生產其他 Bean。
- &beanName 是訪問 FactoryBean 本身的“密鑰”。
浙公網安備 33010602011771號