蛟当罟 发表于 2025-9-25 10:46:19

FactoryBean 和BeanFactory的傻傻的总是分不清?

引言

记得很久以前经常被问到这样一个面试题"FactoryBean 和BeanFactory它们有啥区别"。在 Spring 框架中,BeanFactory 和 FactoryBean 是两个核心概念,虽然名称相似,但它们的角色和功能完全不同。
1. 定义与角色

维度BeanFactoryFactoryBean角色Spring 的 IoC 容器核心接口,负责管理所有 Bean 的生命周期(创建、配置、依赖注入)。一个 特殊 Bean 接口,用于动态创建复杂对象(如代理对象、连接池、动态代理等)。功能提供容器基础能力(如 getBean()、containsBean() 等)。通过 getObject() 方法返回实际需要的 Bean 实例。接口方法getBean()、containsBean()、isSingleton() 等。getObject()、getObjectType()、isSingleton()。典型实现DefaultListableBeanFactory、ApplicationContext 等。SqlSessionFactoryBean(MyBatis)、ProxyFactoryBean(AOP)等。2. 核心区别

特性BeanFactoryFactoryBean获取对象的方式直接返回容器中注册的 Bean 实例(如 getBean("beanName"))。默认返回 getObject() 的结果(如 getBean("factoryBeanName"))。访问自身的方式直接通过 getBean("beanName") 获取。需通过 &beanName 前缀获取(如 getBean("&factoryBeanName"))。是否单例容器默认管理 Bean 的作用域(如单例、原型)。通过 isSingleton() 方法定义创建对象的作用域。使用场景管理所有 Bean 的基础设施(如依赖注入、生命周期管理)。封装复杂对象的创建逻辑(如动态代理、数据库连接池)。3. 使用场景与示例

(1) BeanFactory 的使用


[*]作用:作为 Spring 容器的根接口,负责管理所有 Bean 的生命周期。
[*]示例:// 通过 BeanFactory 获取 Bean
BeanFactory factory = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = factory.getBean("userService", UserService.class);
(2) FactoryBean 的使用


[*]作用:通过自定义 getObject() 方法创建复杂对象。
[*]示例:// 定义 FactoryBean
public class MyConnectionFactory implements FactoryBean<Connection> {
    @Override
    public Connection getObject() throws Exception {
<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>// 返回数据库连接
<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>return DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb");
    }

    @Override
    public Class<?> getObjectType() {
<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>return Connection.class;
    }

    @Override
    public boolean isSingleton() {
<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>return true; // 是否为单例
    }
}

// 配置到 Spring 容器
@Configuration
public class AppConfig {
    @Bean
    public FactoryBean<Connection> connectionFactory() {
<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>return new MyConnectionFactory();
    }
}

// 使用
@Autowired
private Connection connection; // 实际注入的是 getObject() 返回的 Connection
在 Spring 框架中,FactoryBean 被广泛用于集成第三方中间件或框架,通过封装复杂对象的创建逻辑,简化配置并提高灵活性。以下是几个常见中间件使用 FactoryBean 的示例:
1. MyBatis 的 SqlSessionFactoryBean

作用:
创建 MyBatis 的 SqlSessionFactory 实例,集成数据库配置、映射文件扫描等逻辑。
代码示例:
<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>实现原理:

[*]SqlSessionFactoryBean 实现了 FactoryBean。
[*]getObject() 方法内部调用 MyBatis 的 SqlSessionFactoryBuilder 创建 SqlSessionFactory。
[*]支持延迟加载和复杂配置(如多数据源、事务管理器)。
2. OpenFeign 的 FeignClientFactoryBean

作用:
动态创建 Feign 客户端(RESTful API 调用代理对象)。
代码示例:
@Configurationpublic class FeignConfig {    @Bean    public FactoryBean myServiceClient() {<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>FeignClientFactoryBean factory = new FeignClientFactoryBean();<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>factory.setUrl("http://example.com/api");<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>factory.setType(MyServiceClient.class);<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>return factory;    }}实现原理:

[*]FeignClientFactoryBean 封装了 Feign 的 Target 和 Encoder/Decoder 配置。
[*]getObject() 返回动态代理的 Feign 客户端实例。
[*]支持自定义拦截器、重试策略等。
3. Redis 的 RedisConnectionFactoryBean

作用:
创建 Redis 连接池(如 JedisConnectionFactory 或 LettuceConnectionFactory)。
代码示例:
<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>实现原理:

[*]JedisConnectionFactory 本身实现了 FactoryBean。
[*]getObject() 返回 RedisConnection 实例(如 JedisConnection)。
[*]支持连接池配置(如最大连接数、超时时间)。
4. RocketMQ 的 RocketMQTemplate

作用:
封装 RocketMQ 生产者和消费者的创建逻辑。
代码示例:
@Configurationpublic class RocketMQConfig {    @Bean    public RocketMQTemplate rocketMQTemplate() {<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>RocketMQTemplate template = new RocketMQTemplate();<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>template.setProducer(new DefaultMQProducer("my-producer-group"));<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>template.setConsumer(new DefaultMQPushConsumer("my-consumer-group"));<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>return template;    }}实现原理:

[*]RocketMQTemplate 通过 FactoryBean 模式初始化生产者和消费者。
[*]getObject() 返回配置好的 RocketMQTemplate 实例。
[*]支持消息发送、监听器注册等操作。
5. Quartz 的 SchedulerFactoryBean

作用:
创建 Quartz 调度器(Scheduler),集成任务调度逻辑。
代码示例:
<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean><bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean><bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean><bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>    实现原理:

[*]SchedulerFactoryBean 封装了 Quartz 的 SchedulerFactory 配置。
[*]getObject() 返回 Scheduler 实例。
[*]支持动态注册任务和触发器。
6. Dubbo 的 ServiceBean

作用:
发布 Dubbo 服务,封装服务暴露和注册逻辑。
代码示例:
<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>实现原理:

[*]ServiceBean 实现了 FactoryBean。
[*]getObject() 返回服务导出器(Exporter),完成服务注册和暴露。
[*]支持负载均衡、容错策略等 Dubbo 特性。
7. Kafka 的 KafkaTemplate

作用:
封装 Kafka 生产者和消费者的创建逻辑。
代码示例:
@Configurationpublic class KafkaConfig {    @Bean    public KafkaTemplate kafkaTemplate() {<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>return new KafkaTemplate(new ProducerFactory());    }}实现原理:

[*]KafkaTemplate 通过 FactoryBean 模式初始化生产者工厂。
[*]getObject() 返回配置好的 KafkaTemplate 实例。
[*]支持消息发送、消费者监听等操作。
总结对比

中间件FactoryBean 类型作用领域核心方法作用MyBatisSqlSessionFactoryBean数据库访问创建 SqlSessionFactoryOpenFeignFeignClientFactoryBeanRESTful 客户端创建 Feign 动态代理RedisJedisConnectionFactory缓存/键值存储创建 Redis 连接池RocketMQRocketMQTemplate消息队列封装生产者/消费者逻辑QuartzSchedulerFactoryBean定时任务调度创建调度器并注册任务DubboServiceBean微服务 RPC发布服务并注册到注册中心KafkaKafkaTemplate消息队列封装生产者/消费者逻辑

[*]解耦配置与逻辑:通过 FactoryBean 将复杂初始化逻辑封装,Spring 容器只需管理 Bean 的声明。
[*]支持动态创建:可根据运行时条件(如环境变量、配置参数)动态生成不同对象。
[*]统一资源管理:集中管理中间件的连接池、配置参数,便于维护和扩展。
4. 获取 FactoryBean 本身


[*]默认行为:getBean("factoryBeanName") 返回的是 FactoryBean.getObject() 的结果。
[*]获取 FactoryBean 实例本身:需在 Bean 名称前加 & 前缀。// 获取 FactoryBean 创建的 Bean
Connection connection = context.getBean("connectionFactory", Connection.class);

// 获取 FactoryBean 实例本身
MyConnectionFactory factoryBean = (MyConnectionFactory) context.getBean("&connectionFactory");
5. 常见问题与解决方案

问题1:混淆 BeanFactory 和 FactoryBean 的功能


[*]解决方案:明确 BeanFactory 是容器,FactoryBean 是创建 Bean 的工具。
问题2:期望获取 FactoryBean 实例却得到其创建的 Bean


[*]示例:// 错误:获取的是 Encryptor 实例,而非 FactoryBean
Encryptor encryptor = factory.getBean("encryptor");

// 正确:添加 "&" 前缀获取 FactoryBean
FactoryBean factoryBean = factory.getBean("&encryptor");
问题3:未正确实现 getObjectType() 导致类型检查失败


[*]修复:确保 getObjectType() 返回准确的类型信息。
6. 高级应用场景

(1) 动态代理生成

public class ServiceProxyFactoryBean implements FactoryBean {    @Override    public MyService getObject() {<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>return (MyService) Proxy.newProxyInstance(<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>    getClass().getClassLoader(),<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>    new Class[]{MyService.class},<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>    (proxy, method, args) -> {<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean><bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>System.out.println("Before method: " + method.getName());<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean><bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>return method.invoke(new MyServiceImpl(), args);<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>    });    }}(2) 延迟初始化

public class LazyInitFactoryBean implements FactoryBean {    private ExpensiveBean instance;    @Override    public ExpensiveBean getObject() {<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>if (instance == null) {<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>    instance = new ExpensiveBean(); // 延迟初始化<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>}<bean id="sqlSessionFactory" >
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>return instance;    }}7. 总结

维度BeanFactoryFactoryBean本质Spring 容器的根接口,管理所有 Bean 的生命周期。一个特殊 Bean,用于封装复杂对象的创建逻辑。获取方式getBean("beanName") 返回容器中的 Bean 实例。getBean("factoryBeanName") 返回 getObject() 的结果,getBean("&factoryBeanName") 返回 FactoryBean 本身。典型用途Spring 容器的基础功能(如依赖注入、生命周期管理)。创建动态代理、连接池、复杂对象等。

[*]BeanFactory 是 Spring 容器本身,负责管理所有 Bean。
[*]FactoryBean 是 容器中的一个 Bean,负责 生产其他 Bean。
[*]&beanName 是访问 FactoryBean 本身的“密钥”。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: FactoryBean 和BeanFactory的傻傻的总是分不清?