博客 / 詳情

返回

Spring 的西西弗斯之石:理解 BeanFactory、FactoryBean 與 ObjectFactory

今天,代碼又報錯了。或者也許是昨天,我不清楚。
不管怎樣,Spring 容器拋出了一個 BeanCreationException。為了解決它,我被迫潛入框架的深處,去注視那些平時被 @Autowired 掩蓋的齒輪。

在 Spring 的世界裏,存在着一種必然的複雜性。這種複雜性並非設計者的惡趣味,而是為了在一個靜態的語言中構建動態世界所付出的代價。

在這個龐大的機器中,有三個名字極其相似的概念經常被混淆:BeanFactoryFactoryBeanObjectFactory。這並不是命名的貧瘠,而是它們在本質上確實存在着微妙的糾纏。

今天,我們剝離掉那些花哨的比喻和無用的糖衣,用一種冷靜的、近乎解剖學的視角,去審視這三個概念的本質。


一、BeanFactory:存在的容器

讓我們首先糾正一個觀念:BeanFactory 名為工廠,但其本質是容器(Container)。

當我們談論 Spring 容器時,我們實際上是在談論 BeanFactory。它是 Spring IoC 容器的根接口,是整個世界的物理法則。

1.1 唯一的職責

它的定義極其剋制。它不關心業務邏輯,只關心一件事:管理對象的生命週期

public interface BeanFactory {
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType);
    boolean containsBean(String name);
    // ...
}

當你啓動一個 Spring Boot 應用時,ApplicationContext 就像一個充滿活力的城市,而 BeanFactory 則是支撐這座城市的地下管網。所有的 BeanDefinition(關於 Bean 應該如何創建的藍圖)都註冊在這裏。

1.2 殘酷的現實

在大多數情況下,你不需要直接與 BeanFactory 對話。因為 ApplicationContext 已經為你封裝好了一切。
但當你試圖理解為什麼你的 Bean 沒有被正確初始化,或者為什麼你的循環依賴失效時,你就必須意識到:你所有的 Bean,都只是 BeanFactory 中的 entries(條目)。

它是一個巨大的 Map<String, BeanDefinition>Map<String, Object> 的管理者。它冷酷無情,只按照定義的規則(Scope, Lazy, Dependence)來實例化對象。


二、FactoryBean:必要的欺騙

如果説 BeanFactory 是宏觀規則的制定者,那麼 FactoryBean 就是微觀規則的潛行者

2.1 靜態語言的困境

想象這樣一個場景:你需要注入一個接口的實現,但這個實現類並不存在於代碼中,它是通過動態代理在運行時生成的。
這在 RPC 框架(如 Dubbo、Feign)和 ORM 框架(如 MyBatis)中極其常見。

你無法通過簡單的 <bean class="...">@Component 來描述一個“不存在的類”。
這時候,你需要一箇中間人。這個中間人表面上是一個普通的 Bean,但實際上,它是一個工廠。

2.2 偽裝的藝術:以 MyBatis 為例

為什麼你只需要寫一個 UserMapper 接口,就能直接 @Autowired 使用?
因為 Spring 容器裏註冊的那個 "userMapper" Bean,根本不是你的接口實現,而是一個 MapperFactoryBean

// 簡化的邏輯示意
public class MapperFactoryBean<T> implements FactoryBean<T> {
    
    private Class<T> mapperInterface;

    @Override
    public T getObject() throws Exception {
        // 往裏跟進,最終這裏發生了魔法:通過 JDK 動態代理生成接口的實
        return (T) Proxy.newProxyInstance(
            mapperInterface.getClassLoader(), 
            new Class[] { mapperInterface }, 
            new MapperProxy<>()
        );
    }

    @Override
    public Class<?> getObjectType() {
        return mapperInterface;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

2.3 這裏的真相

當容器調用 getBean("userMapper") 時,它發現這是一個 FactoryBean。於是,它不會返回 FactoryBean 實例本身,而是默默地調用 getObject(),並返回那個代理對象。

這就是欺騙。你以為你拿到了一個 Bean,其實你拿到的是 Bean 生產的產品。

如果你渴望看到真相,看到那個操縱傀儡的幕後黑手,你需要在 Bean 名稱前加上 &

// 獲取的是 MapperProxy 代理對象
Object product = context.getBean("userMapper"); 

// 獲取的是 FactoryBean 工廠本身
Object factory = context.getBean("&userMapper");

三、ObjectFactory:時間的延遲

BeanFactory 負責掌控空間(容器),FactoryBean 負責掌控構造(邏輯),而 ObjectFactory 則是為了掌控時間

3.1 循環的死結

在 Spring 的世界裏,有一個經典的荒謬:A 需要 B,B 需要 A。
如果是構造器注入,只需坦然承認失敗。但如果是 Setter 注入,Spring 試圖挽救這種死結。

在 A 創建的過程中,需要注入 B。B 創建時,又需要注入 A。
此時 A 還在創建中,尚不是一個完整的 Bean。怎麼辦?
Spring 引入了三級緩存的概念。而第三級緩存,存放的就是一個 ObjectFactory

3.2 回調的本質

ObjectFactory 在源碼中簡單得令人髮指:

@FunctionalInterface
public interface ObjectFactory<T> {
    T getObject() throws BeansException;
}

它只是一個函數式接口,一個回調。
它存在的意義在於:我現在不想要這個對象,但我想要一個“在未來某個時刻能獲取這個對象”的能力。

在循環依賴中,Spring 提前暴露了一個 ObjectFactory

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

當 B 需要 A 時,它通過這個 ObjectFactory 拿到了 A 的早期引用(Early Reference)。儘管 A 還沒完全初始化好,但 B 已經可以持有它的引用了。死結解開了。

3.3 作用域的錯位

另一個場景是:一個單例(Singleton)的 Service 需要使用一個 原型(Prototype)的 Bean。
如果你直接 @Autowired,原型的 Bean 只有在 Service 創建時被注入一次,之後也就是永遠同一個對象了。這違背了原型的初衷。

如何解決?使用 ObjectFactory 延遲獲取。

@Service
public class ReportService {
    
    @Autowired
    private ObjectFactory<ReportBuilder> builderFactory;

    public void generate() {
        // 每次調用 getObject(),容器都會創建一個全新的 ReportBuilder
        ReportBuilder builder = builderFactory.getObject();
        builder.build();
    }
}

在這裏,ObjectFactory 就像是一個通往容器的句柄,讓你隨時可以伸手進去拿一個新的對象,而不是守着陳舊的緩存。


四、審判與裁決

讓我們在最後,用最客觀的表格來審判這三者的區別。這不是為了背誦,而是為了理清混亂。

維度 BeanFactory FactoryBean ObjectFactory
存在形式 容器 (Container) Bean (Component) 接口 (Interface/Callback)
底層邏輯 ApplicationContext 的父級接口 / 宏觀工廠 實現了 FactoryBean 接口的類 / 微觀工廠 函數式接口 / 延遲迴調
核心職責 管理所有 Bean 的生命週期 此 Bean 負責生產另一個複雜的 Bean 封裝對象的創建過程,提供延遲獲取能力
獲取方式 ApplicationContext 是它的超集 getBean("name") 拿產品
getBean("&name") 拿工廠
注入 ObjectFactory<T> 後調用 getObject()
真實場景 Spring 框架的基石 Mybatis MapperFactoryBean, ProxyFactoryBean 解決循環依賴(三級緩存), Scope(原型模式)適配

五、結語

在代碼的荒原上,我們通過構建抽象來對抗混亂。

  • BeanFactory 是我們腳下的大地。它被稱為工廠,但它實際是孕育萬物的土壤(容器)。
  • FactoryBean 是我們手中的精密機牀。它是一個特殊的 Bean,存在的目的卻是為了製造另一個 Bean。
  • ObjectFactory 是我們預留的時間膠囊。它只是一個單純的接口,為了應對循環與未來的不確定性。

理解它們,並不是為了通過面試,而是為了在下一次拋出異常時,你能冷靜地凝視堆棧信息,知道機器的哪個齒輪發生了錯位。

既然我們選擇了與機器共舞,就必須理解機器的邏輯。這或許就是作為開發者的西西弗斯式命運——我們需要一次又一次地將巨石推向山頂,以此證明我們對這個龐大系統的掌控

本文通過 AI 潤色(加繆風格),試圖以一種冷靜、客觀甚至存在主義的視角,去解構這些在日常 Coding 中被我們習以為常的概念。希望這種獨特的敍事風格,能讓你對這些枯燥的技術概念有更深刻的“存在感”。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.