1. FactoryBean的本质与设计初衷
Spring框架中有两种Bean容易被初学者混淆:普通Bean和FactoryBean。FactoryBean是一种特殊的工厂模式实现,它本身是一个Bean,同时又作为其他Bean的创建工厂。这种设计源于Spring对复杂对象创建场景的抽象需求。
常规Bean在Spring容器中的生命周期是:定义→创建→初始化→使用→销毁。而FactoryBean的生命周期多了一个工厂方法调用环节,这使得它能够:
- 封装复杂对象的创建逻辑(如MyBatis的SqlSessionFactoryBean)
- 实现按需创建的延迟加载机制
- 对第三方库的非Spring友好API进行适配包装
java复制public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
}
这三个接口方法构成了FactoryBean的核心契约。getObject()返回工厂创建的实际对象,getObjectType()声明返回类型,isSingleton()控制是否为单例。这种设计让FactoryBean在Spring IoC容器中既是一个被管理的Bean,又是其他Bean的生产者。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. FactoryBean的典型使用场景
2.1 集成第三方框架
MyBatis-Spring整合包中的SqlSessionFactoryBean就是经典案例。它封装了SqlSessionFactory的复杂构建过程:
java复制@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath*:mapper/*.xml"));
return factoryBean;
}
这里返回的是SqlSessionFactoryBean实例,但Spring实际注入的是getObject()返回的SqlSessionFactory。这种间接创建方式解耦了配置过程和实际使用。
2.2 动态代理生成
当需要为接口创建代理时,FactoryBean能优雅地处理这种动态生成场景。例如自定义RPC客户端:
java复制public class RpcClientFactoryBean implements FactoryBean<Object> {
private Class<?> interfaceClass;
@Override
public Object getObject() {
return Proxy.newProxyInstance(
interfaceClass.getClassLoader(),
new Class<?>[]{interfaceClass},
new RpcInvocationHandler());
}
// getObjectType()和isSingleton()实现...
}
2.3 条件化Bean创建
结合@Conditional注解可以实现更灵活的Bean创建逻辑:
java复制public class ConditionalFactoryBean implements FactoryBean<MyService>, InitializingBean {
private MyService instance;
private boolean enabled;
public void afterPropertiesSet() {
if(enabled) {
this.instance = new MyServiceImpl();
}
}
@Override
public MyService getObject() {
return instance;
}
// 其他实现...
}
3. FactoryBean的底层工作机制
3.1 容器中的注册与识别
Spring容器在初始化时会对FactoryBean做特殊处理:
- BeanDefinition注册阶段:与普通Bean无异
- 实例化阶段:正常创建FactoryBean实例
- 依赖注入阶段:如果检测到Bean是FactoryBean类型:
- 调用getObjectType()确定实际类型
- 将FactoryBean前缀"&"加入别名集合
- 在单例缓存中注册两个引用:
- factoryBeanName → FactoryBean实例
- "&" + factoryBeanName → FactoryBean实例
3.2 getObject()调用时机
方法触发点主要有三个:
- 依赖注入时:当其他Bean声明对FactoryBean产品的依赖时
- 显式调用getBean()时:ApplicationContext.getBean(factoryBeanName)
- 懒加载初始化时:@Lazy注解修饰的依赖首次被访问时
重要提示:getObject()每次调用是否返回新实例取决于isSingleton()返回值。true返回缓存实例,false每次新建。
3.3 与BeanPostProcessor的交互
FactoryBean会参与完整的Bean生命周期:
mermaid复制// 注:根据规范要求,此处不应包含mermaid图表,改为文字描述
/*
FactoryBean生命周期关键节点:
1. InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
2. 实例化FactoryBean
3. BeanPostProcessor.postProcessAfterInstantiation
4. 属性注入
5. BeanPostProcessor.postProcessBeforeInitialization
6. afterPropertiesSet()
7. init-method
8. BeanPostProcessor.postProcessAfterInitialization
9. 实际使用时调用getObject()
*/
4. 高级应用与实战技巧
4.1 自定义FactoryBean的最佳实践
开发自定义FactoryBean时需要注意:
- 线程安全:确保getObject()实现是线程安全的,特别是非单例模式时
- 空值处理:当配置不完整时,getObject()应抛出明确的异常而非返回null
- 类型检查:getObjectType()应动态反映当前getObject()的实际返回类型
java复制public class SafeFactoryBean implements FactoryBean<ComplexObject>, DisposableBean {
private final AtomicReference<ComplexObject> instance = new AtomicReference<>();
@Override
public ComplexObject getObject() {
ComplexObject obj = instance.get();
if(obj == null) {
synchronized(this) {
obj = instance.get();
if(obj == null) {
obj = createComplexObject();
instance.set(obj);
}
}
}
return obj;
}
private ComplexObject createComplexObject() {
// 复杂的创建逻辑...
}
@Override
public void destroy() {
ComplexObject obj = instance.get();
if(obj != null) {
obj.cleanup();
}
}
}
4.2 与Spring Boot的配合使用
在Spring Boot中,FactoryBean可以完美配合@ConfigurationProperties实现类型安全的配置:
java复制@Bean
@ConfigurationProperties(prefix = "app.datasource")
public DataSourceFactoryBean dataSource() {
return new DataSourceFactoryBean();
}
public class DataSourceFactoryBean implements FactoryBean<DataSource> {
private String url;
private String username;
private String password;
// getters & setters
@Override
public DataSource getObject() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(username);
config.setPassword(password);
return new HikariDataSource(config);
}
// 其他实现...
}
4.3 解决循环依赖问题
当FactoryBean产品参与循环依赖时,需要特别注意:
- 在getObject()中避免调用其他尚未初始化的Bean
- 对于setter注入,考虑使用ObjectProvider延迟注入
- 必要时使用@Lazy注解打破循环
java复制public class ServiceFactoryBean implements FactoryBean<MyService> {
private final ObjectProvider<Dependency> dependencyProvider;
public ServiceFactoryBean(ObjectProvider<Dependency> dependencyProvider) {
this.dependencyProvider = dependencyProvider;
}
@Override
public MyService getObject() {
return new MyServiceImpl(dependencyProvider.getIfAvailable());
}
}
5. 常见问题排查指南
5.1 类型不匹配异常
典型错误:
code复制BeanNotOfRequiredTypeException: Bean named 'myFactoryBean' is expected
to be of type 'com.example.Product' but was actually of type 'com.example.MyFactoryBean'
解决方案:
- 确认注入点是否错误地声明了FactoryBean类型而非其产品类型
- 使用"&"前缀显式获取FactoryBean实例:
java复制@Autowired private MyFactoryBean factoryBean; // 错误 @Autowired @Qualifier("&myFactoryBean") private MyFactoryBean factoryBean; // 正确
5.2 初始化顺序问题
当FactoryBean依赖其他Bean,而其他Bean又依赖FactoryBean产品时,可能导致:
code复制BeanCurrentlyInCreationException: Requested bean is currently in creation
调试技巧:
- 使用@DependsOn明确指定初始化顺序
- 在getObject()方法中添加断点,观察调用栈
- 检查是否在getObject()中触发了其他Bean的初始化
5.3 性能优化建议
对于创建成本高的对象:
- 确保isSingleton()返回true(默认值)
- 考虑双重检查锁模式实现getObject()
- 对于资源密集型对象,实现DisposableBean进行正确释放
java复制public class ExpensiveResourceFactoryBean implements FactoryBean<Resource>, DisposableBean {
private Resource resource;
private final Object lock = new Object();
@Override
public Resource getObject() {
if(resource == null) {
synchronized(lock) {
if(resource == null) {
resource = createResource();
}
}
}
return resource;
}
@Override
public void destroy() {
if(resource != null) {
resource.release();
}
}
}
6. 源码级深度解析
6.1 AbstractFactoryBean的实现艺术
Spring提供了AbstractFactoryBean基类,它实现了:
- 标准的单例控制逻辑
- 完整的生命周期管理
- 类型安全的泛型支持
- 模板方法模式封装
典型继承实现:
java复制public class CustomFactoryBean extends AbstractFactoryBean<MyObject> {
private String configProperty;
public void setConfigProperty(String value) {
this.configProperty = value;
}
@Override
public Class<?> getObjectType() {
return MyObject.class;
}
@Override
protected MyObject createInstance() {
return new MyObject(configProperty);
}
}
6.2 FactoryBean与三级缓存
在Spring解决循环依赖的三级缓存机制中,FactoryBean有特殊处理:
- 第一级缓存(单例对象):存储完全初始化好的FactoryBean实例
- 第二级缓存(早期引用):存储正在创建中的FactoryBean引用
- 第三级缓存(对象工厂):存储FactoryBean的ObjectFactory
关键区别在于:
- 普通Bean:缓存的是Bean实例本身
- FactoryBean:缓存的是getObject()返回的产品对象
6.3 与SmartFactoryBean的进阶配合
SmartFactoryBean接口扩展了更多控制能力:
java复制public interface SmartFactoryBean<T> extends FactoryBean<T> {
boolean isPrototype();
boolean isEagerInit();
}
这允许更精细地控制:
- 是否强制原型模式(覆盖isSingleton())
- 是否在启动时立即初始化(而非懒加载)
7. 现代Spring中的新变化
7.1 与Spring Boot自动配置的整合
Spring Boot 2.4+对FactoryBean的自动配置支持更完善:
- 条件报告更友好:/actuator/conditions端点会显示FactoryBean相关条件
- 绑定提示更智能:@ConfigurationProperties绑定失败时会提示可能的FactoryBean方案
- 启动时验证:在上下文刷新阶段提前检查FactoryBean配置有效性
7.2 响应式编程中的适配
在WebFlux环境中使用FactoryBean需要注意:
- getObject()不应阻塞:对于需要IO操作的创建过程,应返回Mono/Flux包装
- 考虑实现ReactiveFactoryBean:
java复制public interface ReactiveFactoryBean<T> extends FactoryBean<Mono<T>> {
default Class<?> getObjectType() {
return Mono.class;
}
}
7.3 与Spring AI的集成模式
在AI组件集成场景中,FactoryBean能很好处理模型加载:
java复制public class ModelFactoryBean implements FactoryBean<PredictionModel>, InitializingBean {
private ModelConfig config;
private PredictionModel model;
public void afterPropertiesSet() {
this.model = loadModel(config);
}
@Override
public PredictionModel getObject() {
return model;
}
private PredictionModel loadModel(ModelConfig config) {
// 耗时的模型加载逻辑...
}
}
这种模式将昂贵的模型加载过程提前到启动阶段,避免首次请求时的延迟。
