1. 简介
工厂方法(Factory Method)是一种非常实用的设计模式,它能把复杂的对象创建逻辑封装在一个方法里,对外提供简单调用接口✅。在 Spring 中,虽然我们日常更多使用构造器注入或字段注入来创建 Bean,但其实还有一种更灵活的方式 —— 通过工厂方法创建 Spring Bean。
本文将深入讲解如何使用实例工厂方法和静态工厂方法来创建 Spring Bean,涵盖无参和带参场景,帮你掌握这一“冷门但关键”的技能,尤其在集成遗留系统或处理复杂初始化逻辑时非常有用。
💡 什么时候该用工厂方法?
当你的对象创建过程涉及多步骤初始化、条件判断、资源加载、或调用非 Spring 管理的第三方工厂时,用工厂方法能有效解耦配置与创建逻辑。
2. 实例工厂方法(Instance Factory Method)
实例工厂方法指的是:先有一个工厂类的实例,再通过它的某个普通方法(非静态)来创建目标 Bean。Spring 会先管理工厂类,再用它生成最终 Bean。
2.1 无参创建
先定义一个简单的 Foo
类,作为我们要创建的 Bean:
public class Foo {}
接着写一个工厂类 InstanceFooFactory
,里面提供一个 createInstance()
方法用于创建 Foo
实例:
public class InstanceFooFactory {
public Foo createInstance() {
return new Foo();
}
}
关键来了,Spring 配置要分两步走:
- 先把工厂类注册成 Bean ✅
- 再定义目标 Bean,通过
factory-bean
指定工厂实例,factory-method
指定创建方法
对应的 XML 配置如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 工厂类本身也是一个 Bean -->
<bean id="instanceFooFactory"
class="com.example.factory.InstanceFooFactory" />
<!-- 使用工厂方法创建 Foo -->
<bean id="foo"
factory-bean="instanceFooFactory"
factory-method="createInstance" />
</beans>
测试一下是否成功注入:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/factorymethod/instance-config.xml")
public class InstanceFooFactoryIntegrationTest {
@Autowired
private Foo foo;
@Test
public void givenValidInstanceFactoryConfig_whenCreateFooInstance_thenInstanceIsNotNull() {
assertNotNull(foo); // ✅ 成功创建
}
}
2.2 带参创建
更常见的情况是,目标对象需要传参。比如我们有一个 Bar
类,构造时需要一个 name
:
public class Bar {
private String name;
public Bar(String name) {
this.name = name;
}
// getter / setter 省略
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
对应的工厂类 InstanceBarFactory
提供一个带参的 createInstance()
方法:
public class InstanceBarFactory {
public Bar createInstance(String name) {
return new Bar(name);
}
}
XML 配置中使用 <constructor-arg>
传参(名字有点误导,其实这里不是构造器参数,而是工厂方法参数)⚠️:
<beans ...>
<bean id="instanceBarFactory"
class="com.example.factory.InstanceBarFactory" />
<bean id="bar"
factory-bean="instanceBarFactory"
factory-method="createInstance">
<constructor-arg value="someName" />
</bean>
</beans>
测试验证参数是否正确传递:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/factorymethod/instance-bar-config.xml")
public class InstanceBarFactoryIntegrationTest {
@Autowired
private Bar instance;
@Test
public void givenValidInstanceFactoryConfig_whenCreateInstance_thenNameIsCorrect() {
assertNotNull(instance);
assertEquals("someName", instance.getName()); // ✅ 参数生效
}
}
⚠️ 踩坑提示:
constructor-arg
这个标签名容易让人误解,其实它在这里是给工厂方法传参,而不是调用目标类的构造函数。如果方法参数多,还可以用index
或type
区分。
3. 静态工厂方法(Static Factory Method)
静态工厂方法不需要先创建工厂实例,直接通过类名调用静态方法即可创建 Bean。适用于工具类、单例工厂等场景。
✅ 优势:配置更简洁,无需提前注册工厂 Bean
❌ 缺点:难以管理工厂状态,不利于测试和扩展,建议优先使用实例工厂
3.1 无参创建
我们沿用 Foo
类,创建一个单例工厂 SingletonFooFactory
:
public class SingletonFooFactory {
private static final Foo INSTANCE = new Foo();
public static Foo createInstance() {
return INSTANCE;
}
}
配置更简单了,只需要指定 class
和 factory-method
即可:
<beans ...>
<bean id="foo"
class="com.example.factory.SingletonFooFactory"
factory-method="createInstance" />
</beans>
测试注入是否成功:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/factorymethod/static-foo-config.xml")
public class SingletonFooFactoryIntegrationTest {
@Autowired
private Foo singleton;
@Test
public void givenValidStaticFactoryConfig_whenCreateInstance_thenInstanceIsNotNull() {
assertNotNull(singleton); // ✅
}
}
3.2 带参创建
虽然静态工厂不推荐修改内部状态,但 Spring 依然支持传参。例如我们想动态设置 Bar
的名字:
public class SingletonBarFactory {
private static final Bar INSTANCE = new Bar("unnamed");
public static Bar createInstance(String name) {
INSTANCE.setName(name); // ⚠️ 修改单例状态!危险操作
return INSTANCE;
}
}
配置传参方式与实例工厂一致:
<beans ...>
<bean id="bar"
class="com.example.factory.SingletonBarFactory"
factory-method="createInstance">
<constructor-arg value="someName" />
</bean>
</beans>
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/factorymethod/static-bar-config.xml")
public class SingletonBarFactoryIntegrationTest {
@Autowired
private Bar instance;
@Test
public void givenValidStaticFactoryConfig_whenCreateInstance_thenNameIsCorrect() {
assertNotNull(instance);
assertEquals("someName", instance.getName()); // ✅
}
}
⚠️ 踩坑警告:上面这种写法虽然能跑通,但存在严重问题 —— 多个地方调用会覆盖单例状态,导致不可预测行为。生产环境应避免此类设计,建议改为每次返回新实例,或改用实例工厂。
4. 总结
方式 | 是否需要工厂 Bean | 适用场景 | 推荐度 |
---|---|---|---|
实例工厂方法 | ✅ 需要 | 复杂创建逻辑、依赖注入、可测试性强 | ✅✅✅ 强烈推荐 |
静态工厂方法 | ❌ 不需要 | 遗留系统集成、工具类、单例工厂 | ✅ 仅限必要时使用 |
✅ 核心要点回顾:
- 工厂方法适合封装复杂创建逻辑,提升配置灵活性
- 实例工厂更符合 Spring 的设计哲学,易于管理依赖
- 静态工厂虽简洁,但状态管理风险高,慎用
<constructor-arg>
实际上传的是工厂方法参数,不是构造器参数,别被名字骗了
📌 最佳实践:优先使用实例工厂 + 构造注入,保持无状态、高内聚、易测试。
本文所有代码示例已整理至 GitHub:https://github.com/spring-example/factory-method-demo