引言

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

1. 定义与角色

维度 BeanFactory FactoryBean
角色 Spring 的 IoC 容器核心接口,负责管理所有 Bean 的生命周期(创建、配置、依赖注入)。 一个 特殊 Bean 接口,用于动态创建复杂对象(如代理对象、连接池、动态代理等)。
功能 提供容器基础能力(如 getBean()containsBean() 等)。 通过 getObject() 方法返回实际需要的 Bean 实例。
接口方法 getBean()containsBean()isSingleton() 等。 getObject()getObjectType()isSingleton()
典型实现 DefaultListableBeanFactoryApplicationContext 等。 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 的 TargetEncoder/Decoder 配置。
  • getObject() 返回动态代理的 Feign 客户端实例。
  • 支持自定义拦截器、重试策略等。

3. Redis 的 RedisConnectionFactoryBean

作用

创建 Redis 连接池(如 JedisConnectionFactoryLettuceConnectionFactory)。

代码示例

<!-- 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 容器的基础功能(如依赖注入、生命周期管理)。 创建动态代理、连接池、复杂对象等。
  • BeanFactorySpring 容器本身,负责管理所有 Bean。
  • FactoryBean容器中的一个 Bean,负责 生产其他 Bean
  • &beanName 是访问 FactoryBean 本身的“密钥”。

FactoryBean 和BeanFactory的傻傻的总是分不清?的更多相关文章

  1. [转帖]十分钟快速理解DPI和PPI,不再傻傻分不清!

    十分钟快速理解DPI和PPI,不再傻傻分不清! https://baijiahao.baidu.com/s?id=1605834796518990333&wfr=spider&for= ...

  2. Beta版本 为什么程序员总是分不清万圣节和圣诞节?因为 Oct 31 == Dec 25。

    Beta版本 软件的测试版本,经常在各类著名软件中的前期发布版本的后缀中可见,包括大名鼎鼎的windows系统,这个阶段的版本一直加入一些新的功能. 软件测试可分为alpha测试.beta测试和联合测 ...

  3. 学点经济学:M0、M1、M2、M3,傻傻分不清?(转载)

    来源:http://t.10jqka.com.cn/pid_97006727.shtml 学点经济学:M0.M1.M2.M3,傻傻分不清? 25,508人浏览 2018-08-03 11:06 常听人 ...

  4. session cookie傻傻分不清

    做了这么多年测试,还是分不清什么是cookie,什么是session?很正常,很多初级开发工程师可能到现在都搞不清什么是session,cookie相对来说会简单很多. 下面这篇文章希望能够帮助大家分 ...

  5. 还分不清 Cookie、Session、Token、JWT?一篇文章讲清楚

    还分不清 Cookie.Session.Token.JWT?一篇文章讲清楚 转载来源 公众号:前端加加 作者:秋天不落叶 什么是认证(Authentication) 通俗地讲就是验证当前用户的身份,证 ...

  6. 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). ...

  7. 【jvm】08-垃圾回收器那么多傻傻分不清?

    [jvm]08-垃圾回收器那么多傻傻分不清? 欢迎关注b站账号/公众号[六边形战士夏宁],一个要把各项指标拉满的男人.该文章已在github目录收录. 屏幕前的大帅比和大漂亮如果有帮助到你的话请顺手点 ...

  8. Spring高级特性之四:FactoryBean和BeanFactory

    FactoryBean和BeanFactory两只是两个单词顺序不同但是内容大不相同.落脚点在后面一个单词,前面一个单词是其功能描述:FactoryBean--工厂bean,一个建工厂的bean?Be ...

  9. cookie、session,、token,还在傻傻分不清?

    摘要:session 和 token 本质上是没有区别的,都是对用户身份的认证机制,只是他们实现的校验机制不一样而已. 本文分享自华为云社区<Session/Cookie/Token 还傻傻分不 ...

  10. Qemu,KVM,Virsh傻傻的分不清

    当你安装了一台Linux,想启动一个KVM虚拟机的时候,你会发现需要安装不同的软件,启动虚拟机的时候,有多种方法: virsh start kvm命令 qemu命令 qemu-kvm命令 qemu-s ...

随机推荐

  1. Obsidian 笔记一键转换发布为 Jekyll 博客

    Obsidian 是一款功能强大且灵活的知识管理和笔记软件,与 Jekyll 这一轻量级静态博客框架的结合,既能保留 Obsidian 的网状知识关联优势,又能借助 Jekyll 的高效编译能力快速生 ...

  2. 模型即产品?从应用角度看AI产品发展趋势

    提供AI咨询+AI项目陪跑服务,有需要回复1 在ChatGPT发布后的两年里,AI应用层的发展可以用一个词来评价不温不火,到去年年初时甚至有些疲软的情况.其原因有三点: 第一,算力不足,模型API响应 ...

  3. 【Linux】5.6 Shell打印输出指令

    Shell打印输出命令 1. echo命令 Shell 的 echo 指令与 PHP 的 echo 指令类似,都是用于字符串的输出.命令格式:echo string 您可以使用echo实现更复杂的输出 ...

  4. 【网络协议】ANT风格路径匹配

    我们在看java技术书籍的过程中,当加载文件时总会遇到是否支持ant风格路径加载,这里说的ant风格是什么意思呢,今天我查了一下,明白了什么意思,现在总结一下. Ant风格,为请求路径的一种匹配方式. ...

  5. ASP.NET 简单实现数字时钟

    每隔1000毫秒(1s)获取一次当前时间 <asp:ScriptManager ID="ScriptManager_Time" runat="server" ...

  6. Git放弃本地修改,强制拉取最新版

    git fetch –-all git reset –-hard origin/master git fetch : 下载远程的库的内容(不做合并): git reset :指令把HEAD指向mast ...

  7. 为啥有好多人说 Arduino 是玩具?

    作为一个在嵌入式领域摸爬滚打近十年的老兵,我不得不说这个问题触动了我的神经.每次听到有人说"Arduino只是个玩具",我都忍不住想反驳,但冷静下来思考后,我发现这个问题并不是非黑 ...

  8. MySQL 中 int(11) 的 11 表示什么?

    MySQL 中 int(11) 的 11 表示什么? 在 MySQL 中,int(11) 中的 11 并不表示整数的取值范围,而是用于显示宽度(Display Width).它的含义和具体影响如下: ...

  9. 解决 podman 容器无法在宿主机和容器内部相互访问问题的记录

    解决 podman 容器无法在宿主机和容器内部相互访问问题的记录 近期在使用 podman 时,遇到了容器无法在宿主机和容器内部相互访问的问题.经过一番探索,参考了这篇文章,成功解决了该问题.在此,我 ...

  10. ElementUI默认样式修改

    修改ElementUI 样式的几种方式 新建全局样式表 新建 global.css 文件,并在 main.js 中引入. global.css 文件一般都放在 src->assets 静态资源文 ...