本文基于Spring 5.3.33源码解析,带你从浅入深探索FactoryBean的核心机制,重点剖析factoryBeanObjectCache的运作原理,并揭示SmartFactoryBean的高级特性。
一、FactoryBean:Spring中的对象工厂
1.1 FactoryBean基础概念
FactoryBean是Spring框架中一种特殊类型的Bean,它本身不直接提供功能,而是负责创建其他Bean的实例。这种设计模式在Spring中被广泛应用,尤其适用于复杂对象的创建场景。
public interface FactoryBean<T> {
// 返回工厂创建的对象实例
T getObject() throws Exception;
// 返回对象类型
Class<?> getObjectType();
// 是否为单例对象
boolean isSingleton();
}
1.2 为何需要FactoryBean?
当遇到以下场景时,FactoryBean就大显身手了:
- 创建过程复杂的对象(如数据库连接池)
- 需要动态决定对象类型的场景
- 需要封装第三方库的初始化过程
- 对象创建需要特殊配置
二、factoryBeanObjectCache:核心存储容器
2.1 神秘的缓存容器
在Spring源码深处,隐藏着一个关键缓存容器——factoryBeanObjectCache
。这个位于FactoryBeanRegistrySupport
类中的缓存负责存储FactoryBean创建的对象实例。
// 源码位置:org.springframework.beans.factory.support.FactoryBeanRegistrySupport
public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {
// FactoryBean创建的单例对象缓存
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);
// ... 其他代码
}
2.2 缓存的工作机制
当通过getBean()
获取FactoryBean产品时,Spring会按以下逻辑处理:
三、何时使用factoryBeanObjectCache?
3.1 缓存写入条件
对象会被存储到factoryBeanObjectCache
中,当且仅当同时满足以下条件:
- FactoryBean是单例(
isSingleton()
返回true) - 当前作用域是单例(非原型作用域)
- 对象首次被创建
3.2 缓存读取流程
从源码角度看缓存读取过程:
// 源码位置:FactoryBeanRegistrySupport
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName) {
// 单例FactoryBean的处理
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
// 尝试从缓存获取
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 创建对象并放入缓存
object = doGetObjectFromFactoryBean(factory, beanName);
this.factoryBeanObjectCache.put(beanName, object);
}
return object;
}
}
else {
// 非单例情况直接创建新对象
return doGetObjectFromFactoryBean(factory, beanName);
}
}
3.3 不会缓存的情况
以下情况对象不会被缓存:
- 原型作用域的FactoryBean(每次请求都创建新对象)
public boolean isSingleton() { return false; // 原型模式 }
- FactoryBean本身是原型作用域
- 在Bean初始化过程中出现异常
- SmartFactoryBean指定不缓存(通过特殊配置)
四、SmartFactoryBean:更智能的工厂
4.1 升级版FactoryBean
SmartFactoryBean在基础FactoryBean上增加了更多控制能力:
public interface SmartFactoryBean<T> extends FactoryBean<T> {
// 是否立即初始化(在应用启动时创建)
default boolean isEagerInit() {
return false;
}
// 是否原型对象(已废弃,使用作用域代替)
@Deprecated
default boolean isPrototype() {
return false;
}
}
4.2 高级特性解析
特性1:立即初始化(Eager Initialization)
设置isEagerInit()
返回true时,Spring会在容器启动阶段就初始化该对象,而不是等到首次请求。
public class EagerInitFactoryBean implements SmartFactoryBean<MyBean> {
@Override
public boolean isEagerInit() {
return true; // 容器启动时立即初始化
}
// 其他方法实现...
}
特性2:原型作用域控制
虽然isPrototype()
方法已被废弃,但我们可以结合@Scope
注解实现原型控制:
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeFactoryBean implements FactoryBean<PrototypeBean> {
// 实现方法...
}
4.3 源码中的SmartFactoryBean
在Spring容器启动过程中,SmartFactoryBean会被特殊处理:
// 源码位置:AbstractApplicationContext
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 处理SmartFactoryBean的立即初始化
String[] smartFactoryNames = beanFactory.getBeanNamesForType(SmartFactoryBean.class);
for (String name : smartFactoryNames) {
SmartFactoryBean<?> smartFactory = (SmartFactoryBean<?>) beanFactory.getBean("&" + name);
if (smartFactory.isEagerInit()) {
// 触发立即初始化
beanFactory.getBean(name);
}
}
}
五、FactoryBean的实战应用
5.1 典型使用场景
public class ConnectionFactoryBean implements FactoryBean<Connection> {
private String url;
private String username;
private String password;
@Override
public Connection getObject() throws Exception {
return DriverManager.getConnection(url, username, password);
}
// 其他方法实现...
}
5.2 性能优化建议
- 对于重量级对象使用单例FactoryBean+缓存
- 对于轻量级可变对象使用原型作用域
- 避免在getObject()中执行耗时操作
- 合理使用SmartFactoryBean的立即初始化特性
六、总结与思考
通过本文的探讨,我们深入理解了Spring FactoryBean的核心机制:
- factoryBeanObjectCache是存储单例FactoryBean产品的核心缓存
- 缓存使用遵循严格的单例双重检查逻辑
- SmartFactoryBean提供了更精细的控制能力
- 原型FactoryBean的产品不会进入缓存
- 立即初始化特性影响容器启动行为
最后思考一个问题:为什么Spring要设计factoryBeanObjectCache这个单独的缓存,而不是使用普通的单例缓存?
答案在于FactoryBean的特殊性。FactoryBean本身是一个Bean,而它创建的产品是另一个逻辑Bean。使用单独的缓存可以:
- 清晰区分FactoryBean实例和它创建的产品
- 避免循环依赖时的复杂情况
- 支持特殊处理逻辑(如&前缀获取工厂本身)
// 获取FactoryBean本身(注意&前缀)
FactoryBean<?> factory = context.getBean("&myFactoryBean");
// 获取FactoryBean创建的产品
Object product = context.getBean("myFactoryBean");
理解FactoryBean的底层机制,能够帮助我们更好地设计复杂的Spring应用,优化性能,并避免潜在的陷阱。