Spring 的西西弗斯之石:理解 BeanFactory、FactoryBean 与 ObjectFactory
<p>今天,代码又报错了。或者也许是昨天,我不清楚。<br>不管怎样,Spring 容器抛出了一个 <code>BeanCreationException</code>。为了解决它,我被迫潜入框架的深处,去注视那些平时被 <code>@Autowired</code> 掩盖的齿轮。</p>
<p>在 Spring 的世界里,存在着一种必然的复杂性。这种复杂性并非设计者的恶趣味,而是为了在一个静态的语言中构建动态世界所付出的代价。</p>
<p>在这个庞大的机器中,有三个名字极其相似的概念经常被混淆:<code>BeanFactory</code>、<code>FactoryBean</code> 和 <code>ObjectFactory</code>。这并不是命名的贫瘠,而是它们在本质上确实存在着微妙的纠缠。</p>
<p>今天,我们剥离掉那些花哨的比喻和无用的糖衣,用一种冷静的、近乎解剖学的视角,去审视这三个概念的本质。</p>
<hr>
<h2 id="一beanfactory存在的容器">一、BeanFactory:存在的容器</h2>
<p><strong>让我们首先纠正一个观念:BeanFactory 名为工厂,但其本质是容器(Container)。</strong></p>
<p>当我们谈论 Spring 容器时,我们实际上是在谈论 <code>BeanFactory</code>。它是 Spring IoC 容器的根接口,是整个世界的物理法则。</p>
<h3 id="11-唯一的职责">1.1 唯一的职责</h3>
<p>它的定义极其克制。它不关心业务逻辑,只关心一件事:<strong>管理对象的生命周期</strong>。</p>
<pre><code class="language-java">public interface BeanFactory {
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType);
boolean containsBean(String name);
// ...
}
</code></pre>
<p>当你启动一个 Spring Boot 应用时,<code>ApplicationContext</code> 就像一个充满活力的城市,而 <code>BeanFactory</code> 则是支撑这座城市的地下管网。所有的 BeanDefinition(关于 Bean 应该如何创建的蓝图)都注册在这里。</p>
<h3 id="12-残酷的现实">1.2 残酷的现实</h3>
<p>在大多数情况下,你不需要直接与 <code>BeanFactory</code> 对话。因为 <code>ApplicationContext</code> 已经为你封装好了一切。<br>
但当你试图理解为什么你的 Bean 没有被正确初始化,或者为什么你的循环依赖失效时,你就必须意识到:<strong>你所有的 Bean,都只是 <code>BeanFactory</code> 中的 entries(条目)。</strong></p>
<p>它是一个巨大的 <code>Map<String, BeanDefinition></code> 和 <code>Map<String, Object></code> 的管理者。它冷酷无情,只按照定义的规则(Scope, Lazy, Dependence)来实例化对象。</p>
<hr>
<h2 id="二factorybean必要的欺骗">二、FactoryBean:必要的欺骗</h2>
<p>如果说 <code>BeanFactory</code> 是宏观规则的制定者,那么 <code>FactoryBean</code> 就是微观规则的<strong>潜行者</strong>。</p>
<h3 id="21-静态语言的困境">2.1 静态语言的困境</h3>
<p>想象这样一个场景:<strong>你需要注入一个接口的实现,但这个实现类并不存在于代码中,它是通过动态代理在运行时生成的。</strong><br>
这在 RPC 框架(如 Dubbo、Feign)和 ORM 框架(如 MyBatis)中极其常见。</p>
<p>你无法通过简单的 <code><bean class="..."></code> 或 <code>@Component</code> 来描述一个“不存在的类”。<br>
这时候,你需要一个中间人。这个中间人表面上是一个普通的 Bean,但实际上,它是一个工厂。</p>
<h3 id="22-伪装的艺术以-mybatis-为例">2.2 伪装的艺术:以 MyBatis 为例</h3>
<p>为什么你只需要写一个 <code>UserMapper</code> 接口,就能直接 <code>@Autowired</code> 使用?<br>
因为 Spring 容器里注册的那个 "userMapper" Bean,根本不是你的接口实现,而是一个 <code>MapperFactoryBean</code>。</p>
<pre><code class="language-java">// 简化的逻辑示意
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;
}
}
</code></pre>
<h3 id="23-这里的真相">2.3 这里的真相</h3>
<p>当容器调用 <code>getBean("userMapper")</code> 时,它发现这是一个 <code>FactoryBean</code>。于是,它不会返回 <code>FactoryBean</code> 实例本身,而是默默地调用 <code>getObject()</code>,并返回那个代理对象。</p>
<p>这就是欺骗。<strong>你以为你拿到了一个 Bean,其实你拿到的是 Bean 生产的产品。</strong></p>
<p>如果你渴望看到真相,看到那个操纵傀儡的幕后黑手,你需要在 Bean 名称前加上 <code>&</code>:</p>
<pre><code class="language-java">// 获取的是 MapperProxy 代理对象
Object product = context.getBean("userMapper");
// 获取的是 FactoryBean 工厂本身
Object factory = context.getBean("&userMapper");
</code></pre>
<hr>
<h2 id="三objectfactory时间的延迟">三、ObjectFactory:时间的延迟</h2>
<p><code>BeanFactory</code> 负责掌控空间(容器),<code>FactoryBean</code> 负责掌控构造(逻辑),而 <code>ObjectFactory</code> 则是为了掌控<strong>时间</strong>。</p>
<h3 id="31-循环的死结">3.1 循环的死结</h3>
<p>在 Spring 的世界里,有一个经典的荒谬:A 需要 B,B 需要 A。<br>
如果是构造器注入,只需坦然承认失败。但如果是 Setter 注入,Spring 试图挽救这种死结。</p>
<p>在 A 创建的过程中,需要注入 B。B 创建时,又需要注入 A。<br>
此时 A 还在创建中,尚不是一个完整的 Bean。怎么办?<br>
Spring 引入了三级缓存的概念。而第三级缓存,存放的就是一个 <code>ObjectFactory</code>。</p>
<h3 id="32-回调的本质">3.2 回调的本质</h3>
<p><code>ObjectFactory</code> 在源码中简单得令人发指:</p>
<pre><code class="language-java">@FunctionalInterface
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}
</code></pre>
<p>它只是一个<strong>函数式接口</strong>,一个回调。<br>
它存在的意义在于:<strong>我现在不想要这个对象,但我想要一个“在未来某个时刻能获取这个对象”的能力。</strong></p>
<p>在循环依赖中,Spring 提前暴露了一个 <code>ObjectFactory</code>:</p>
<pre><code class="language-java">addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
</code></pre>
<p>当 B 需要 A 时,它通过这个 <code>ObjectFactory</code> 拿到了 A 的早期引用(Early Reference)。尽管 A 还没完全初始化好,但 B 已经可以持有它的引用了。死结解开了。</p>
<h3 id="33-作用域的错位">3.3 作用域的错位</h3>
<p>另一个场景是:一个单例(Singleton)的 Service 需要使用一个 原型(Prototype)的 Bean。<br>
如果你直接 <code>@Autowired</code>,原型的 Bean 只有在 Service 创建时被注入一次,之后也就是永远同一个对象了。这违背了原型的初衷。</p>
<p>如何解决?使用 <code>ObjectFactory</code> 延迟获取。</p>
<pre><code class="language-java">@Service
public class ReportService {
@Autowired
private ObjectFactory<ReportBuilder> builderFactory;
public void generate() {
// 每次调用 getObject(),容器都会创建一个全新的 ReportBuilder
ReportBuilder builder = builderFactory.getObject();
builder.build();
}
}
</code></pre>
<p>在这里,<strong><code>ObjectFactory</code> 就像是一个通往容器的句柄,让你随时可以伸手进去拿一个新的对象,而不是守着陈旧的缓存。</strong></p>
<hr>
<h2 id="四审判与裁决">四、审判与裁决</h2>
<p>让我们在最后,用最客观的表格来审判这三者的区别。这不是为了背诵,而是为了理清混乱。</p>
<table>
<thead>
<tr>
<th style="text-align: left">维度</th>
<th style="text-align: left">BeanFactory</th>
<th style="text-align: left">FactoryBean</th>
<th style="text-align: left">ObjectFactory</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><strong>存在形式</strong></td>
<td style="text-align: left"><strong>容器</strong> (Container)</td>
<td style="text-align: left"><strong>Bean</strong> (Component)</td>
<td style="text-align: left"><strong>接口</strong> (Interface/Callback)</td>
</tr>
<tr>
<td style="text-align: left"><strong>底层逻辑</strong></td>
<td style="text-align: left"><code>ApplicationContext</code> 的父级接口 / <strong>宏观工厂</strong></td>
<td style="text-align: left">实现了 <code>FactoryBean</code> 接口的类 / <strong>微观工厂</strong></td>
<td style="text-align: left">函数式接口 / <strong>延迟回调</strong></td>
</tr>
<tr>
<td style="text-align: left"><strong>核心职责</strong></td>
<td style="text-align: left">管理所有 Bean 的生命周期</td>
<td style="text-align: left">此 Bean 负责<strong>生产</strong>另一个复杂的 Bean</td>
<td style="text-align: left">封装对象的创建过程,提供<strong>延迟</strong>获取能力</td>
</tr>
<tr>
<td style="text-align: left"><strong>获取方式</strong></td>
<td style="text-align: left"><code>ApplicationContext</code> 是它的超集</td>
<td style="text-align: left"><code>getBean("name")</code> 拿产品<br><code>getBean("&name")</code> 拿工厂</td>
<td style="text-align: left">注入 <code>ObjectFactory<T></code> 后调用 <code>getObject()</code></td>
</tr>
<tr>
<td style="text-align: left"><strong>真实场景</strong></td>
<td style="text-align: left">Spring 框架的基石</td>
<td style="text-align: left">Mybatis <code>MapperFactoryBean</code>, <code>ProxyFactoryBean</code></td>
<td style="text-align: left">解决循环依赖(三级缓存), Scope(原型模式)适配</td>
</tr>
</tbody>
</table>
<h2 id="五结语">五、结语</h2>
<p>在代码的荒原上,我们通过构建抽象来对抗混乱。</p>
<ul>
<li><strong>BeanFactory</strong> 是我们脚下的大地。<strong>它被称为工厂,但它实际是孕育万物的土壤(容器)。</strong></li>
<li><strong>FactoryBean</strong> 是我们手中的精密机床。<strong>它是一个特殊的 Bean,存在的目的却是为了制造另一个 Bean。</strong></li>
<li><strong>ObjectFactory</strong> 是我们预留的时间胶囊。<strong>它只是一个单纯的接口,为了应对循环与未来的不确定性。</strong></li>
</ul>
<p>理解它们,并不是为了通过面试,而是为了在下一次抛出异常时,你能冷静地凝视堆栈信息,知道机器的哪个齿轮发生了错位。</p>
<p>既然我们选择了与机器共舞,就必须理解机器的逻辑。这或许就是作为开发者的西西弗斯式命运——<strong>我们需要一次又一次地将巨石推向山顶,以此证明我们对这个庞大系统的掌控</strong>。</p>
<blockquote>
<p><strong>本文通过 AI 润色(加缪风格),试图以一种冷静、客观甚至存在主义的视角,去解构这些在日常 Coding 中被我们习以为常的概念。希望这种独特的叙事风格,能让你对这些枯燥的技术概念有更深刻的“存在感”。</strong></p>
</blockquote><br><br>
来源:https://www.cnblogs.com/xzqcsj/p/19479429
頁:
[1]