Spring FactoryBean 核心机制
一、核心概念区分
在理解 FactoryBean 时,需先明确两个关键对象的区别,避免混淆:
| 对象类型 | 定义 | 默认特性 |
|---|---|---|
| FactoryBean 自身 | 实现 FactoryBean<T> 接口的“工厂 bean”,负责生产目标 bean |
作用域默认是 singleton(容器启动时初始化自身实例,仅一次) |
| FactoryBean 生产的目标 bean | 由 FactoryBean 的 getObject() 方法返回的最终业务对象(如 MyBean) |
作用域由 isSingleton() 控制,默认返回 true(即单例)
|
二、getObject() 方法执行时机
getObject() 是 FactoryBean 生产目标 bean 的核心方法,其执行时机分「默认场景」和「特殊场景」,核心取决于目标 bean 的获取方式和作用域。
1. 默认场景(目标 bean 为单例,isSingleton()=true)
这是最常见的场景,getObject() 遵循「按需执行」原则,不会在容器启动时立即执行,而是延迟到「首次需要目标 bean 时」触发,具体分两种触发方式:
(1)通过 getBean() 主动获取目标 bean 时
- 容器启动阶段:仅初始化 FactoryBean 自身实例,不调用
getObject()。 - 首次调用
context.getBean(目标bean类型/名称)时:触发getObject()生成目标 bean,生成后存入容器缓存。 - 后续调用
getBean():直接从缓存获取目标 bean,不再执行getObject()。
示例代码流程:
// 1. 容器启动:仅初始化 MyFactoryBean 实例,不打印日志
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2. 首次 getBean:触发 getObject(),打印"getObject() 被调用"
MyBean bean1 = context.getBean(MyBean.class);
// 3. 后续 getBean:直接用缓存,不打印日志
MyBean bean2 = context.getBean(MyBean.class);
(2)通过 @Autowired 注入目标 bean 时
getObject() 的执行时机取决于「被注入类的初始化时机」:
- 若被注入类(如
ServiceA)是单例(默认):容器启动时初始化ServiceA,执行依赖注入时触发getObject()生成目标 bean,注入后缓存目标 bean。 - 若被注入类是 prototype:每次创建
ServiceA实例时,执行依赖注入才触发getObject()(若目标 bean 也是 prototype,则每次都生成新实例)。
示例代码流程:
// 被注入类(单例)
@Component
public class ServiceA {
// 注入 FactoryBean 生产的 MyBean
@Autowired
private MyBean myBean;
}
// 容器启动时:
// 1. 初始化 MyFactoryBean(自身)
// 2. 初始化 ServiceA,执行依赖注入
// 3. 触发 MyFactoryBean 的 getObject() 生成 MyBean,注入到 ServiceA
2. 特殊场景(目标 bean 为原型,isSingleton()=false)
若重写 isSingleton() 并返回 false,目标 bean 为原型作用域:
- 无论通过
getBean()还是@Autowired,每次获取/注入目标 bean 时,都会执行一次getObject(),生成新的目标 bean 实例,不缓存。
三、关键对比:普通 Bean vs FactoryBean 生产的 Bean
当两种方式都生成同一个类型的 Bean(如 BeanA)时,核心区别在于「Bean 的创建时机」:
| 对比维度 | 普通 Bean(直接定义,如 @Component/@Bean) |
FactoryBean 生产的 Bean |
|---|---|---|
| 创建时机 | 容器启动阶段(单例默认预初始化),早于依赖注入 | 延迟到首次获取/注入时(getObject() 执行时) |
| 核心逻辑 | 直接由 Spring 容器实例化、初始化 | 由 FactoryBean 的 getObject() 自定义创建逻辑(支持复杂对象) |
| 适用场景 | 简单对象(无复杂创建逻辑) | 复杂对象(如依赖多步骤构建、第三方组件集成等) |
四、核心结论
- FactoryBean 自身默认是单例,容器启动时初始化,但不立即生成目标 bean。
- 默认场景(目标 bean 单例)下,
getObject()延迟执行,触发时机是「首次获取/注入目标 bean 时」。 - 普通 Bean 比 FactoryBean 生产的 Bean 更早创建(容器启动阶段 vs 首次使用时)。
- 目标 bean 的作用域由
isSingleton()控制,决定getObject()是执行一次(单例)还是多次(原型)。