把两个词倒过来念,很多人就分不清了。BeanFactory 是容器——你向它要 Bean;FactoryBean 是被容器管理的工厂——它生产另一个对象。一个是入口,一个是插件,名字相似纯属历史巧合。
真正绊倒人的不是定义,而是三个后果:getBean("x") 拿到的可能不是那个 Bean、&x 加在哪、类型注入为什么失效。代码按 Spring Framework 6.1 / Spring Boot 3.3.x 核对。
1. BeanFactory:容器的最小接口
BeanFactory 是 IoC 容器的最底层契约,核心就两件事:按名字/类型取 Bean、查询 Bean 的元信息。
| 能力 | BeanFactory |
ApplicationContext(继承并扩展) |
|---|---|---|
getBean(name/type) |
有 | 有 |
| 单例缓存、作用域 | 有 | 有 |
层级容器(getParentBeanFactory) |
有 | 有 |
自动注册 BeanPostProcessor |
有 | 有 |
自动注册 BeanFactoryPostProcessor |
不自动 | 自动 |
国际化 MessageSource |
无 | 有 |
事件 ApplicationEventPublisher |
无 | 有 |
资源加载 ResourcePatternResolver |
无 | 有 |
@Autowired 注解处理(AutowiredAnnotationBeanPostProcessor) |
需手动注册 | 自动 |
启动即实例化单例(refresh()) |
无 | 有 |
BeanFactory bf = new DefaultListableBeanFactory(); // 裸容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); // 完整容器
结论:业务代码别注入 BeanFactory 做依赖装配,它拿不到注解驱动的能力,等于把 Spring 降级成手写工厂;只有写框架层(如 BeanFactoryPostProcessor)时它才是对的参数类型。
层级容器:getBean 找不到时只向上找父容器,父容器不向下找——这就是"子容器能看到父容器的 Bean,反之不行"的机制。
2. FactoryBean:容器里的一台"生产机"
FactoryBean<T> 只有三个方法:
| 方法 | 返回 | 作用 | 不实现会怎样 |
|---|---|---|---|
T getObject() |
产品对象 | 真正被注入的东西 | 必须实现 |
Class<?> getObjectType() |
产品类型 | 类型匹配的依据 | 默认返回 null,类型注入失效 |
boolean isSingleton() |
是否单例 | 产品是否缓存 | 默认 true(产品被缓存) |
public class RetryProxyFactoryBean implements FactoryBean<OrderService> {
private final OrderService target;
public RetryProxyFactoryBean(OrderService target) { this.target = target; }
@Override public OrderService getObject() {
return (OrderService) Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[]{OrderService.class},
(p, m, a) -> { try { return m.invoke(target, a); }
catch (InvocationTargetException e) { throw e.getCause(); } });
}
@Override public Class<?> getObjectType() { return OrderService.class; }
@Override public boolean isSingleton() { return true; }
}
注册后,容器里有两条"路径"指向同一个定义:
getBean("retryProxy")→ 返回getObject()的产物(那个代理对象);getBean("&retryProxy")→ 返回RetryProxyFactoryBean本身。
System.out.println(ctx.getBean("retryProxy").getClass()); // jdk.proxy1.$Proxy52
System.out.println(ctx.getBean("&retryProxy").getClass()); // com.example.RetryProxyFactoryBean
& 前缀是 BeanFactory.FACTORY_BEAN_PREFIX,不是语法糖,而是容器区分"要产品"和"要工厂"的唯一开关。
| 调用 | 拿到 | 适用场景 |
|---|---|---|
getBean("x") |
getObject() 的返回值 |
业务注入 |
getBean("&x") |
FactoryBean 实例 |
取工厂做配置、调工厂自定义方法 |
getBean(x.class) |
若产品类型匹配则返回产品 | 类型注入 |
getBean(&x.class) |
不合法 | 前缀只用于按名查找 |
3. 谁在用 FactoryBean
| 组件 | FactoryBean 实现 |
生产什么 |
|---|---|---|
| MyBatis | MapperFactoryBean |
Mapper 接口的动态代理 |
| Spring ORM | LocalContainerEntityManagerFactoryBean |
EntityManagerFactory |
| Spring ORM | SqlSessionFactoryBean |
SqlSessionFactory |
| AOP | ProxyFactoryBean |
声明式代理对象 |
| 远程调用 | RmiProxyFactoryBean、HessianProxyFactoryBean |
远程服务本地代理 |
共同特征:最终对象需要复杂装配或被包装,而这个过程依赖容器环境。以 MyBatis 为例:
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.example.mapper.UserMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
// 从这个定义里取出来的直接就是可用的 Mapper 代理
UserMapper mapper = ctx.getBean(UserMapper.class);
这也解释了那个常见困惑:"Mapper 是接口、没写实现,为什么能 @Autowired?"——容器注册的是 MapperFactoryBean,注入的是它的产品。
4. FactoryBean 与 @Bean 方法:何时还需要前者
@Bean 方法已经能返回任意对象,绝大多数场景它更简洁。两者真正的差别在类型已知性与作用域语义:
| 维度 | @Bean 方法 |
FactoryBean |
|---|---|---|
| 声明位置 | @Configuration 类里的方法 |
一个实现类 + 注册 |
| 返回对象类型 | 由方法返回类型决定 | 由 getObjectType() 运行期决定(可动态) |
| 是否多一层间接 | 否,方法返回即产品 | 是,产品由 getObject() 产生 |
&name 取工厂 |
不适用(除非返回的实例本身是 FactoryBean) | 适用 |
| 适合场景 | 静态装配、一次性构造 | 产品的类型/数量在运行期才确定、需要被复用为基础设施 |
| 生命周期耦合 | 低 | 高,可实现 InitializingBean |
| 生态兼容 | 需要自己处理 | MyBatis/ORM/AOP 等既有组件已按此约定实现 |
判断规则两条:产品类型在编译期确定、且不需被别的组件按"工厂"识别 → @Bean;产品类型在运行期才确定、或要对接按 FactoryBean 约定工作的既有组件 → FactoryBean。
需要提醒的是:@Bean 方法返回 FactoryBean 实例时,容器同样会走 FactoryBean 语义——因为容器是在拿到实例后用 instanceof FactoryBean 判断的,与定义方式无关。此时想要工厂本身,依然要写 &name。
5. 三个容易混的名字
| 名字 | 属于哪一层 | 时机 | 能做什么 | 典型用途 |
|---|---|---|---|---|
FactoryBean |
Bean 定义层 | 取 Bean 时 | 决定"这个 Bean 是什么" | 代理、适配、动态类型 |
BeanFactoryPostProcessor |
Bean 定义层 | 所有 Bean 实例化之前 | 改 BeanDefinition(作用域、属性、开关) |
占位符解析、条件注册、动态改配置 |
BeanPostProcessor |
Bean 实例层 | 每个 Bean 初始化前后 | 包装、代理、注入增强 | @Autowired 处理、AOP 代理、@PostConstruct |
一句话:BeanFactoryPostProcessor 动"蓝图",BeanPostProcessor 动"成品",FactoryBean 决定"成品从哪来"。
6. SmartFactoryBean 与类型推断
SmartFactoryBean<T> 是 FactoryBean 的子接口,多出两个方法:
| 方法 | 含义 | 不实现(默认) |
|---|---|---|
isEagerInit() |
容器启动时就调用 getObject() |
false,等第一次被用到才创建产品 |
isPrototype() |
产品是否 prototype 语义 | false |
public class ExpensiveBeanFactoryBean implements SmartFactoryBean<ExpensiveBean> {
@Override public ExpensiveBean getObject() { return build(); }
@Override public Class<?> getObjectType() { return ExpensiveBean.class; }
@Override public boolean isEagerInit() { return true; } // 启动即预热
}
isEagerInit 用于启动期预热:把首次调用的代价挪到启动阶段,代价是启动变慢、构建失败即启动失败。
类型查询则更微妙:
String[] names = ctx.getBeanNamesForType(DataSource.class);
String[] fast = ctx.getBeanNamesForType(DataSource.class, true, false); // allowEagerInit=false
getBeanNamesForType(type) 对 FactoryBean 的默认行为是问 getObjectType();返回 null 时,在允许 eager init 的情况下容器会实例化工厂并调 getObject() 探明类型,可能触发昂贵初始化甚至循环依赖——allowEagerInit=false 就是关掉这个探测。
7. 常见坑
| 坑 | 现象 | 原因 | 处理 |
|---|---|---|---|
漏写 & |
拿到的是代理/产品,调用工厂自定义方法报 ClassCastException |
getBean("x") 走的是产品路径 |
显式 getBean("&x") |
getObjectType() 返回 null |
按类型注入失败、NoSuchBeanDefinitionException |
容器无法判断产品类型 | 返回明确类型;泛型可用 ResolvableType |
getObject() 里做重活 |
启动慢或首次请求卡顿 | 创建时机被 isEagerInit 或 eager 探测触发 |
懒建,或用 ObjectProvider |
在 getObject() 里依赖自身产品 |
BeanCurrentlyInCreationException |
工厂与产品互相依赖 | 拆成两个 Bean,或注入 ObjectProvider |
把 FactoryBean 当 BeanFactoryPostProcessor 用 |
改不了其他 Bean 的定义 | 时机与职责都不同 | 改定义用 BeanFactoryPostProcessor |
最后一条是"名字混淆"的典型事故:想在启动时替换某个 Bean 的实现,却写了 FactoryBean,结果只换掉自己产的这一个产品。
8. 边界:什么时候不该用 FactoryBean
| 场景 | 更合适的做法 | 理由 |
|---|---|---|
| 只是构造一个复杂对象 | @Bean 方法 + 构造器注入 |
少一层间接,栈浅、易读 |
| 需要按环境返回不同实现 | @Conditional / @Profile |
声明式判断比运行期分支清晰 |
| 需要动态数量的 Bean | ImportBeanDefinitionRegistrar |
面向定义层而非产品层 |
| 只是想要一个静态工厂方法 | @Bean 里直接调它 |
已经够了 |
| 需要 AOP 代理 | @Aspect 或 @Transactional |
已有成熟代理机制,不必手搓 |
一句话:FactoryBean 是给"产品类型在运行期才确定、且需要被基础设施识别"的场景准备的;其余情况,@Bean 方法几乎总是更短的路径。
9. 自测
getBean("userMapper")与getBean("&userMapper")分别返回什么?BeanFactory与ApplicationContext最关键的能力差异是哪三条?FactoryBean.getObjectType()返回null会带来哪些具体后果?@Bean方法返回一个FactoryBean实例时,容器注入的到底是什么?FactoryBean、BeanFactoryPostProcessor、BeanPostProcessor各自的作用时机是什么?SmartFactoryBean.isEagerInit()返回true会影响启动速度还是首次请求,为什么?