news 2026/5/9 15:10:28

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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就像是一个通往容器的句柄,让你随时可以伸手进去拿一个新的对象,而不是守着陈旧的缓存。


四、审判与裁决

让我们在最后,用最客观的表格来审判这三者的区别。这不是为了背诵,而是为了理清混乱。

维度BeanFactoryFactoryBeanObjectFactory
存在形式容器(Container)Bean(Component)接口(Interface/Callback)
底层逻辑ApplicationContext的父级接口 /宏观工厂实现了FactoryBean接口的类 /微观工厂函数式接口 /延迟回调
核心职责管理所有 Bean 的生命周期此 Bean 负责生产另一个复杂的 Bean封装对象的创建过程,提供延迟获取能力
获取方式ApplicationContext是它的超集getBean("name")拿产品
getBean("&name")拿工厂
注入ObjectFactory<T>后调用getObject()
真实场景Spring 框架的基石MybatisMapperFactoryBean,ProxyFactoryBean解决循环依赖(三级缓存), Scope(原型模式)适配

五、结语

在代码的荒原上,我们通过构建抽象来对抗混乱。

  • BeanFactory是我们脚下的大地。它被称为工厂,但它实际是孕育万物的土壤(容器)。
  • FactoryBean是我们手中的精密机床。它是一个特殊的 Bean,存在的目的却是为了制造另一个 Bean。
  • ObjectFactory是我们预留的时间胶囊。它只是一个单纯的接口,为了应对循环与未来的不确定性。

理解它们,并不是为了通过面试,而是为了在下一次抛出异常时,你能冷静地凝视堆栈信息,知道机器的哪个齿轮发生了错位。

既然我们选择了与机器共舞,就必须理解机器的逻辑。这或许就是作为开发者的西西弗斯式命运——我们需要一次又一次地将巨石推向山顶,以此证明我们对这个庞大系统的掌控

本文通过 AI 润色(加缪风格),试图以一种冷静、客观甚至存在主义的视角,去解构这些在日常 Coding 中被我们习以为常的概念。希望这种独特的叙事风格,能让你对这些枯燥的技术概念有更深刻的“存在感”。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 15:10:05

基于单片机汽车速度控制系统设计

**单片机设计介绍&#xff0c;基于单片机汽车速度控制系统设计 文章目录一 概要二、功能设计设计思路三、 软件设计原理图五、 程序一 概要 基于单片机汽车速度控制系统设计概要如下&#xff1a; 一、设计目标 本设计的目标是开发一款基于单片机的汽车速度控制系统&#xff0c…

作者头像 李华
网站建设 2026/5/4 0:56:58

从青少年亚军到成人组第六名:韩宁波的羽球人生没有「天花板」

从青少年亚军到成人组第六名&#xff1a;韩宁波的羽球人生没有「天花板」在宁夏羽毛球运动的版图上&#xff0c;韩宁波的名字始终与突破、坚守和超越紧密相连。从青少年赛场的亚军到成人组第六名的跨越&#xff0c;他以二十年如一日的热爱与坚持&#xff0c;书写了一段没有「天…

作者头像 李华
网站建设 2026/5/4 0:57:11

JavaFX-制作安装包

JavaFX-制作安装包1、时光如梭&#xff0c;一转眼就是两三年&#xff0c;记得当时把打包的javafx程序制作了安装包。时间一长&#xff0c;又忘得差不多了&#xff0c;在此赶快记录一下。将上一篇打包的可执行.exe和其依赖的环境一起制作成一个安装包&#xff0c;看起来逼格更高…

作者头像 李华
网站建设 2026/5/5 10:55:45

Access自动生成PPT报告完全指南

hi&#xff0c;大家好&#xff01;在日常工作中&#xff0c;我们经常需要将Access数据库中的数据整理成PPT报告进行汇报。手工复制粘贴不仅效率低下&#xff0c;还容易出错。本文将手把手教你使用VBA实现Access数据自动导出到PowerPoint&#xff0c;生成一份专业的数据分析报告…

作者头像 李华
网站建设 2026/5/3 16:59:36

剧本杀狼人杀小程序开发全解析:玩法落地+架构支撑+实时交互优化

剧本杀&狼人杀小程序的核心竞争力是“沉浸式交互实时协作/博弈”&#xff0c;其在线化改造直接解决线下“组局难、地域受限”痛点&#xff0c;单款爆款月活可破百万。但超70%开发者因“实时语音延迟、匹配卡顿、对局状态错乱”等问题导致用户流失&#xff0c;核心玩法的技术…

作者头像 李华