前言
在 Spring 的日常开发中,我们通常使用 @Component、@Service 或者 @Configuration + @Bean 的方式来定义 Bean。对于大多数简单的对象(比如 Controller、Service),这些方式非常直观且高效。
但是,假设我们需要创建一个初始化过程非常复杂的对象(例如:需要读取复杂的配置文件、需要连接第三方服务、或者需要动态代理生成的对象),如果把这坨复杂的构建代码全都塞在 @Bean 方法里,代码会显得非常臃肿且难以复用。
这时候,Spring 为我们提供了一个强大的接口——FactoryBean。
今天我们就来聊聊 FactoryBean 是什么,以及它如何帮助我们优雅地创建复杂对象。
什么是 FactoryBean?
FactoryBean 是 Spring 容器中一个特殊的接口。
- 普通 Bean:Spring 容器直接利用反射调用构造器创建出的对象。
- Factory Bean:它也是一个 Bean,但它是一个**“生产 Bean 的 Bean”**。把它注入容器后,当你向容器索要这个 Bean 时,Spring 不会给你 FactoryBean 本身,而是会调用它的 getObject() 方法,把你真正想要的对象给你。
简单来说:FactoryBean 就像一个工厂里的“外包工人”,Spring 容器(大工厂)把他招进来,不是为了让他当管理层,而是为了让他专门生产某种特定的复杂产品。
接口定义
FactoryBean 的源码非常简洁,核心就三个方法:
java public interface FactoryBean<T> { // 1. 返回真正需要的对象(核心逻辑写在这里) @Nullable T getObject() throws Exception; // 2. 返回对象的类型 @Nullable Class<?> getObjectType(); // 3. 是否是单例(默认为 true) default boolean isSingleton() { return true; } }
实战演练:模拟一个复杂的 Connection 对象
假设我们需要创建一个 MyConnection 对象,它的创建过程很麻烦,需要拼接 URL 和加载驱动(这里模拟复杂逻辑)。
1. 定义目标对象
这是一个普通的 Java 类:
java public class MyConnection { private String connectionInfo; public void connect() { System.out.println("连接成功,信息:" + connectionInfo); } // 省略 getter/setter public void setConnectionInfo(String connectionInfo) { this.connectionInfo = connectionInfo; } }
2. 实现 FactoryBean
我们创建一个工厂 Bean,专门用来生产 MyConnection。
java import org.springframework.beans.factory.FactoryBean; import org.springframework.stereotype.Component; @Component // 这里的名字是 "myConnectionFactoryBean" public class MyConnectionFactoryBean implements FactoryBean<MyConnection> { @Override public MyConnection getObject() throws Exception { // 这里可以写非常复杂的逻辑 System.out.println(">>> FactoryBean 开始复杂的造车过程..."); MyConnection conn = new MyConnection(); // 模拟复杂初始化 String config = "jdbc:mysql://localhost:3306/mydb"; conn.setConnectionInfo("解密后的配置: " + config); return conn; } @Override public Class<?> getObjectType() { return MyConnection.class; } @Override public boolean isSingleton() { return true; // 确保是单例 } }
3. 测试获取
这是最神奇的地方。注意看我们 getBean 的时候,填入的 ID 是实现类的名字(首字母小写),但拿到的却是 getObject() 返回的对象。
java @SpringBootTest class FactoryBeanTest { @Autowired private ApplicationContext applicationContext; @Test void testGetBean() { // 1. 获取 FactoryBean 生产的对象 Object bean = applicationContext.getBean("myConnectionFactoryBean"); System.out.println("获取到的对象类型: " + bean.getClass()); if (bean instanceof MyConnection) { ((MyConnection) bean).connect(); } } }
输出结果:
text >>> FactoryBean 开始复杂的造车过程... 获取到的对象类型: class com.example.demo.MyConnection 连接成功,信息:解密后的配置: jdbc:mysql://localhost:3306/mydb
进阶技巧:如何获取 FactoryBean 本身?
有时候,我们确实需要获取那个“造车的工人”(MyConnectionFactoryBean)本身,而不是他造出来的“车”(MyConnection)。
Spring 提供了一个特殊的前缀 &。
java @Test void testGetFactorySelf() { // 加上 & 前缀 Object factoryBean = applicationContext.getBean("&myConnectionFactoryBean"); System.out.println("获取到的对象类型: " + factoryBean.getClass()); // 输出: class com.example.demo.MyConnectionFactoryBean }
核心面试题:BeanFactory 和 FactoryBean 的区别
这是初级到中级工程师面试中出现频率极高的问题。
|
特性 |
BeanFactory |
FactoryBean |
|
本质 |
容器 (Container) |
Bean |
|
角色 |
Spring IoC 容器的顶层接口,它是工厂本身,负责管理所有的 Bean。 |
Spring 中的一种特殊接口,它是一个可以生产对象的 Bean。 |
|
职责 |
负责 Bean 的实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。 |
负责实例化那种逻辑很复杂的 Bean(例如 MyBatis 的 SqlSessionFactoryBean)。 |
|
使用方式 |
context.getBean("xxx") 调用的是 BeanFactory 的方法。 |
用户自定义实现,用来介入 Bean 的创建过程。 |
一句话总结:
- BeanFactory 是 Spring 框架的心脏(大工厂),它装着所有的 Bean。
- FactoryBean 是大工厂里的一个特殊生产线(小工厂),专门用来生产某个复杂的 Bean。
源码中的经典应用
FactoryBean 在开源框架中应用非常广泛,证明了它的实用性:
- MyBatis: SqlSessionFactoryBean。MyBatis 整合 Spring 时,就是通过这个 FactoryBean 创建了 SqlSessionFactory,因为它需要读取 mapper.xml、配置数据源等一堆复杂操作。
- Feign: OpenFeign 在生成接口的动态代理对象时,也是利用了 FactoryBean 的机制。
- Spring AOP: ProxyFactoryBean,用于创建代理对象。
总结
对于初学者来说,掌握 FactoryBean 有助于理解 Spring 是如何整合第三方框架的。当你遇到以下场景时,请想起它:
- 对象的创建逻辑非常复杂,用 xml 或者 @Bean 代码太乱。
- 你需要引用第三方库的类,但没法直接修改源码加注解。
- 你需要动态代理生成某个接口的实现类注入到容器中。
希望这篇博客能帮你解开 FactoryBean 的面纱!
56

被折叠的 条评论
为什么被折叠?



