引言

记得很久以前经常被问到这样一个面试题"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. Linux centos 运行telnet命令command not found的解决方法

      Linux centos 运行telnet命令,出现下面的错误提示: 1 2 [root@localhost ~]# telnet 127.0.0.1 -bash: telnet: command ...

  2. UML中的各种关系

    各种关系 UML中的各种关系一览表 名称 英文名称 符号 描述 实现方法 耦合强度 举例 关键词 备注 依赖 dependency 1.当类与类之间有使用关系时就属于依赖关系:2.依赖不具有" ...

  3. 【SpringCloud】SpringCloud Alibaba Sentinel实现熔断与限流

    SpringCloud Alibaba Sentinel实现熔断与限流 限流与降级 限流 blockHandler 降级 fallback 降级需要运行时出现异常才会触发,而限流一旦触发,你连运行的机 ...

  4. 【SpringCloud】SpringCloud Bus消息总线

    SpringCloud Bus消息总线 概述 上一讲解的加深和扩充,一言以蔽之 分布式自动刷新配置功能 Spring Cloud Bus配合Spring Cloud Config使用可以实现配置的动态 ...

  5. 如何开发 MCP 服务?保姆级教程!

    最近这段时间有个 AI 相关的概念特别火,叫 MCP,全称模型上下文协议(Model Context Protocol).这是由 Anthropic 推出的一项开放标准,目标是为大型语言模型和 AI ...

  6. MySQL 中的数据排序是怎么实现的?

    MySQL 中的数据排序实现 在 MySQL 中,数据排序是通过 ORDER BY 子句实现的,主要涉及 文件排序 和 索引排序 两种方式. 1. 排序的基本原理 MySQL 的排序实现分为以下两种情 ...

  7. [笔记]通过命令行连接MySQL数据库服务器的几种方式总结如下

    通过命令行连接MySQL数据库服务器的几种方式总结如下: 1.连接本地数据库,用户名为"root",密码"123456"(注意:"-p"和& ...

  8. AutoFac(三)——装配扫描(批量注册之扫描类型)

    一.装配扫描 Autofac允许通过常规组装的方式去注册组件(构造方法.实例.lambda表达式等),您可以扫描和注册单个类型,也可以具体的扫描Autofac模块去注册. 1.扫描类型 除了已知的的常 ...

  9. NOIP集训 P4137 Rmq Problem / mex 题解

    前置指使:可持久化线段树 题解:P4137 Rmq Problem / mex 有一个长度为 \(n\) 的数组 \(\{ a_1,a_2,...,a_n \}\) . \(m\) 次询问,每次询问一 ...

  10. Java--事务,操作数据库,实现转账

    更新:2019/3/29 目录 简介 事务的四个特性 一个小Demo 目录结构 jdbc.properties JDBCUtil.java TestTransaction.java[核心代码] 数据库 ...