FactoryBean 和BeanFactory的傻傻的总是分不清?
引言
记得很久以前经常被问到这样一个面试题"FactoryBean 和BeanFactory它们有啥区别"。在 Spring 框架中,BeanFactory 和 FactoryBean 是两个核心概念,虽然名称相似,但它们的角色和功能完全不同。
1. 定义与角色
| 维度 | BeanFactory | FactoryBean |
|---|---|---|
| 角色 | Spring 的 IoC 容器核心接口,负责管理所有 Bean 的生命周期(创建、配置、依赖注入)。 | 一个 特殊 Bean 接口,用于动态创建复杂对象(如代理对象、连接池、动态代理等)。 |
| 功能 | 提供容器基础能力(如 getBean()、containsBean() 等)。 |
通过 getObject() 方法返回实际需要的 Bean 实例。 |
| 接口方法 | getBean()、containsBean()、isSingleton() 等。 |
getObject()、getObjectType()、isSingleton()。 |
| 典型实现 | DefaultListableBeanFactory、ApplicationContext 等。 |
SqlSessionFactoryBean(MyBatis)、ProxyFactoryBean(AOP)等。 |
2. 核心区别
| 特性 | BeanFactory | FactoryBean |
|---|---|---|
| 获取对象的方式 | 直接返回容器中注册的 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 {
// 返回数据库连接
return DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb");
} @Override
public Class<?> getObjectType() {
return Connection.class;
} @Override
public boolean isSingleton() {
return true; // 是否为单例
}
} // 配置到 Spring 容器
@Configuration
public class AppConfig {
@Bean
public FactoryBean<Connection> connectionFactory() {
return new MyConnectionFactory();
}
} // 使用
@Autowired
private Connection connection; // 实际注入的是 getObject() 返回的 Connection
在 Spring 框架中,FactoryBean 被广泛用于集成第三方中间件或框架,通过封装复杂对象的创建逻辑,简化配置并提高灵活性。以下是几个常见中间件使用 FactoryBean 的示例:
1. MyBatis 的 SqlSessionFactoryBean
作用:
创建 MyBatis 的 SqlSessionFactory 实例,集成数据库配置、映射文件扫描等逻辑。
代码示例:
<!-- Spring 配置文件中定义 SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
实现原理:
SqlSessionFactoryBean实现了FactoryBean<SqlSessionFactory>。getObject()方法内部调用 MyBatis 的SqlSessionFactoryBuilder创建SqlSessionFactory。- 支持延迟加载和复杂配置(如多数据源、事务管理器)。
2. OpenFeign 的 FeignClientFactoryBean
作用:
动态创建 Feign 客户端(RESTful API 调用代理对象)。
代码示例:
@Configuration
public class FeignConfig {
@Bean
public FactoryBean<MyServiceClient> myServiceClient() {
FeignClientFactoryBean factory = new FeignClientFactoryBean();
factory.setUrl("http://example.com/api");
factory.setType(MyServiceClient.class);
return factory;
}
}
实现原理:
FeignClientFactoryBean封装了 Feign 的Target和Encoder/Decoder配置。getObject()返回动态代理的 Feign 客户端实例。- 支持自定义拦截器、重试策略等。
3. Redis 的 RedisConnectionFactoryBean
作用:
创建 Redis 连接池(如 JedisConnectionFactory 或 LettuceConnectionFactory)。
代码示例:
<!-- Spring 配置文件中定义 RedisConnectionFactoryBean -->
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="localhost"/>
<property name="port" value="6379"/>
</bean>
实现原理:
JedisConnectionFactory本身实现了FactoryBean<RedisConnection>。getObject()返回RedisConnection实例(如JedisConnection)。- 支持连接池配置(如最大连接数、超时时间)。
4. RocketMQ 的 RocketMQTemplate
作用:
封装 RocketMQ 生产者和消费者的创建逻辑。
代码示例:
@Configuration
public class RocketMQConfig {
@Bean
public RocketMQTemplate rocketMQTemplate() {
RocketMQTemplate template = new RocketMQTemplate();
template.setProducer(new DefaultMQProducer("my-producer-group"));
template.setConsumer(new DefaultMQPushConsumer("my-consumer-group"));
return template;
}
}
实现原理:
RocketMQTemplate通过FactoryBean模式初始化生产者和消费者。getObject()返回配置好的RocketMQTemplate实例。- 支持消息发送、监听器注册等操作。
5. Quartz 的 SchedulerFactoryBean
作用:
创建 Quartz 调度器(Scheduler),集成任务调度逻辑。
代码示例:
<!-- Spring 配置文件中定义 SchedulerFactoryBean -->
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="myCronTrigger"/>
</list>
</property>
</bean>
实现原理:
SchedulerFactoryBean封装了 Quartz 的SchedulerFactory配置。getObject()返回Scheduler实例。- 支持动态注册任务和触发器。
6. Dubbo 的 ServiceBean
作用:
发布 Dubbo 服务,封装服务暴露和注册逻辑。
代码示例:
<!-- Spring 配置文件中定义 Dubbo ServiceBean -->
<bean id="dubboService" class="com.alibaba.dubbo.config.ServiceBean">
<property name="interface" value="com.example.MyService"/>
<property name="ref" ref="myServiceImpl"/>
</bean>
实现原理:
ServiceBean实现了FactoryBean<Exporter>。getObject()返回服务导出器(Exporter),完成服务注册和暴露。- 支持负载均衡、容错策略等 Dubbo 特性。
7. Kafka 的 KafkaTemplate
作用:
封装 Kafka 生产者和消费者的创建逻辑。
代码示例:
@Configuration
public class KafkaConfig {
@Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(new ProducerFactory<>());
}
}
实现原理:
KafkaTemplate通过FactoryBean模式初始化生产者工厂。getObject()返回配置好的KafkaTemplate实例。- 支持消息发送、消费者监听等操作。
总结对比
| 中间件 | FactoryBean 类型 | 作用领域 | 核心方法作用 |
|---|---|---|---|
| MyBatis | SqlSessionFactoryBean |
数据库访问 | 创建 SqlSessionFactory |
| OpenFeign | FeignClientFactoryBean |
RESTful 客户端 | 创建 Feign 动态代理 |
| Redis | JedisConnectionFactory |
缓存/键值存储 | 创建 Redis 连接池 |
| RocketMQ | RocketMQTemplate |
消息队列 | 封装生产者/消费者逻辑 |
| Quartz | SchedulerFactoryBean |
定时任务调度 | 创建调度器并注册任务 |
| Dubbo | ServiceBean |
微服务 RPC | 发布服务并注册到注册中心 |
| Kafka | KafkaTemplate |
消息队列 | 封装生产者/消费者逻辑 |
- 解耦配置与逻辑:通过
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<MyService> {
@Override
public MyService getObject() {
return (MyService) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class[]{MyService.class},
(proxy, method, args) -> {
System.out.println("Before method: " + method.getName());
return method.invoke(new MyServiceImpl(), args);
});
}
}
(2) 延迟初始化
public class LazyInitFactoryBean implements FactoryBean<ExpensiveBean> {
private ExpensiveBean instance;
@Override
public ExpensiveBean getObject() {
if (instance == null) {
instance = new ExpensiveBean(); // 延迟初始化
}
return instance;
}
}
7. 总结
| 维度 | BeanFactory | FactoryBean |
|---|---|---|
| 本质 | Spring 容器的根接口,管理所有 Bean 的生命周期。 | 一个特殊 Bean,用于封装复杂对象的创建逻辑。 |
| 获取方式 | getBean("beanName") 返回容器中的 Bean 实例。 |
getBean("factoryBeanName") 返回 getObject() 的结果,getBean("&factoryBeanName") 返回 FactoryBean 本身。 |
| 典型用途 | Spring 容器的基础功能(如依赖注入、生命周期管理)。 | 创建动态代理、连接池、复杂对象等。 |
- BeanFactory 是 Spring 容器本身,负责管理所有 Bean。
- FactoryBean 是 容器中的一个 Bean,负责 生产其他 Bean。
- &beanName 是访问 FactoryBean 本身的“密钥”。
FactoryBean 和BeanFactory的傻傻的总是分不清?的更多相关文章
- [转帖]十分钟快速理解DPI和PPI,不再傻傻分不清!
十分钟快速理解DPI和PPI,不再傻傻分不清! https://baijiahao.baidu.com/s?id=1605834796518990333&wfr=spider&for= ...
- Beta版本 为什么程序员总是分不清万圣节和圣诞节?因为 Oct 31 == Dec 25。
Beta版本 软件的测试版本,经常在各类著名软件中的前期发布版本的后缀中可见,包括大名鼎鼎的windows系统,这个阶段的版本一直加入一些新的功能. 软件测试可分为alpha测试.beta测试和联合测 ...
- 学点经济学:M0、M1、M2、M3,傻傻分不清?(转载)
来源:http://t.10jqka.com.cn/pid_97006727.shtml 学点经济学:M0.M1.M2.M3,傻傻分不清? 25,508人浏览 2018-08-03 11:06 常听人 ...
- session cookie傻傻分不清
做了这么多年测试,还是分不清什么是cookie,什么是session?很正常,很多初级开发工程师可能到现在都搞不清什么是session,cookie相对来说会简单很多. 下面这篇文章希望能够帮助大家分 ...
- 还分不清 Cookie、Session、Token、JWT?一篇文章讲清楚
还分不清 Cookie.Session.Token.JWT?一篇文章讲清楚 转载来源 公众号:前端加加 作者:秋天不落叶 什么是认证(Authentication) 通俗地讲就是验证当前用户的身份,证 ...
- ASCII、Unicode、UTF-8、UTF-8(without BOM)、UTF-16、UTF-32傻傻分不清
ASCII.Unicode.UTF-8.UTF-8(without BOM).UTF-16.UTF-32傻傻分不清 目录 ASCII.Unicode.UTF-8.UTF-8(without BOM). ...
- 【jvm】08-垃圾回收器那么多傻傻分不清?
[jvm]08-垃圾回收器那么多傻傻分不清? 欢迎关注b站账号/公众号[六边形战士夏宁],一个要把各项指标拉满的男人.该文章已在github目录收录. 屏幕前的大帅比和大漂亮如果有帮助到你的话请顺手点 ...
- Spring高级特性之四:FactoryBean和BeanFactory
FactoryBean和BeanFactory两只是两个单词顺序不同但是内容大不相同.落脚点在后面一个单词,前面一个单词是其功能描述:FactoryBean--工厂bean,一个建工厂的bean?Be ...
- cookie、session,、token,还在傻傻分不清?
摘要:session 和 token 本质上是没有区别的,都是对用户身份的认证机制,只是他们实现的校验机制不一样而已. 本文分享自华为云社区<Session/Cookie/Token 还傻傻分不 ...
- Qemu,KVM,Virsh傻傻的分不清
当你安装了一台Linux,想启动一个KVM虚拟机的时候,你会发现需要安装不同的软件,启动虚拟机的时候,有多种方法: virsh start kvm命令 qemu命令 qemu-kvm命令 qemu-s ...
随机推荐
- centos安装JDK11
(一)建立目录 mkdir /home/jdk11 (二) 上传JDK的离线liunx包 cd /home/jdk11 示例:jdk-11.0.19_linux-x64_bin.tar.gz (三) ...
- Tomcat的优化(分别为操作系统优化(内核参数优化),Tomcat配置文件参数优化,Java虚拟机(JVM)调优)
Tomcat的优化 一.Tomcat 优化 Tomcat 配置文件参数优化 二.系统内核优化 三.Tomcat 配置 JVM 参 ...
- 爬虫管理平台-TaskPyro的任务管理
官网:https://docs.taskpyro.cn/ 任务管理 TaskPyro提供了强大而灵活的任务管理功能,让您能够轻松创建和管理Python脚本的定时任务. 创建任务 在TaskPyro中创 ...
- 【教程】Windows10系统激活
Windows10系统激活 一.找一个激活码 到百度搜索,筛选发表日期在最近一个月或者一周之内的 二.以管理员身份打开cmd 按Win+R键,输入cmd打开命令行窗口 按Ctrl+Shift+Esc键 ...
- leetcode每日一题:对角线上的质数
题目 2614. 对角线上的质数 给你一个下标从 0 开始的二维整数数组 nums . 返回位于 nums 至少一条 对角线 上的最大 质数 .如果任一对角线上均不存在质数,返回 0 . 注意: 如果 ...
- app自动化设计
一.在pom.xml引入依赖 testng:测试框架用例管理 appium:需要用到appium log4j:日志集成 allure:生成报告 二.po分层 分为基础层,page层,用例层,xml文件 ...
- 使用 Go 构建一个最小的 API 应用
最近有项目要使用 Go 开发,作为一个. NET Core 选手,准备先撸一个包含 CRUD 的最小 MVP 项目练手. 要创建一个 TODO 应用,会创建下面这些接口: API Descriptio ...
- Git错误,fatal: The current branch master has no upstream branch. To push the current branch and set the remote as upstream
问题:当我执行git push命令的时候,报错如下: fatal: The current branch master has no upstream branch. To push the curr ...
- 如何将EndNote 和 Word (office)连接起来
1,首先在电脑上打开word2019,点击左上角的"文件"菜单. 2,然后在打开的文件菜单中点击"选项"的快捷链接. 3,接下来在打开的Word选项窗口中点击左 ...
- 【BUG】Python3|安装python3-pip依赖缺失,might want to run ‘apt --fix-broken install‘ to correct these. unment
今天装python,版本装错了. 然后删又删不掉,装pip又装不上,报错是这样的: 想装的时候: 7f2a0f717aa3:~/$ sudo apt-get install python3-pip p ...