引言

記得很久以前經常被問到這樣一個面試題"FactoryBean 和BeanFactory它們有啥區別"。在 Spring 框架中,BeanFactoryFactoryBean 是兩個核心概念,雖然名稱相似,但它們的角色和功能完全不同。

1. 定義與角色

維度 BeanFactory FactoryBean
角色 Spring 的 IoC 容器核心接口,負責管理所有 Bean 的生命周期(創建、配置、依賴注入)。 一個 特殊 Bean 接口,用于動態創建復雜對象(如代理對象、連接池、動態代理等)。
功能 提供容器基礎能力(如 getBean()containsBean() 等)。 通過 getObject() 方法返回實際需要的 Bean 實例。
接口方法 getBean()containsBean()isSingleton() 等。 getObject()getObjectType()isSingleton()
典型實現 DefaultListableBeanFactoryApplicationContext 等。 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 的 TargetEncoder/Decoder 配置。
  • getObject() 返回動態代理的 Feign 客戶端實例。
  • 支持自定義攔截器、重試策略等。

3. Redis 的 RedisConnectionFactoryBean

作用
創建 Redis 連接池(如 JedisConnectionFactoryLettuceConnectionFactory)。

代碼示例

<!-- 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 容器的基礎功能(如依賴注入、生命周期管理)。 創建動態代理、連接池、復雜對象等。
  • BeanFactorySpring 容器本身,負責管理所有 Bean。
  • FactoryBean容器中的一個 Bean,負責 生產其他 Bean
  • &beanName 是訪問 FactoryBean 本身的“密鑰”。