1. 簡介
工廠方法可以作為一種有用的技術,用於將複雜的創建邏輯隱藏在一個方法調用中。
雖然我們通常使用構造函數或字段注入在 Spring 中創建 Bean,我們也可以使用工廠方法創建 Spring Bean。
在本教程中,我們將深入研究使用實例工廠方法和靜態工廠方法創建 Spring Bean。
2. 實例工廠方法
標準實現實例工廠方法模式是創建一個方法,該方法返回所需的 Bean。
此外,我們可以配置 Spring 以使用或不使用參數創建我們的所需 Bean。
2.1. 無參數
我們可以創建一個 Foo 類來表示我們的 Bean 被創建:
public class Foo {}然後,我們創建一個名為 InstanceFooFactory 的類,其中包含一個工廠方法,createInstance,該方法創建我們的 Foo Bean:
public class InstanceFooFactory {
public Foo createInstance() {
return new Foo();
}
}之後,我們配置 Spring:
- 創建一個 Bean,用於我們的工廠類(InstanceFooFactory)
- 使用 factory-bean 屬性引用我們的工廠 Bean
- 使用 factory-method 屬性引用我們的工廠方法(createInstance)
<beans ...>
<bean id="instanceFooFactory"
class="com.baeldung.factorymethod.InstanceFooFactory" />
<bean id="foo"
factory-bean="instanceFooFactory"
factory-method="createInstance" />
</beans>最後,我們自動注入所需的 Foo Bean。Spring 將使用我們的 createInstance 工廠方法創建該 Bean:
@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. 使用參數
我們還可以使用 Spring 配置中的 constructor-arg 元素,向我們的實例工廠方法提供參數。
首先,我們創建一個類,Bar,它使用了參數。
public class Bar {
private String name;
public Bar(String name) {
this.name = name;
}
// ...getters & setters
}接下來,我們創建一個實例工廠類 InstanceBarFactory,該類包含一個工廠方法,它接受一個參數並返回一個 Bar Bean:
public class InstanceBarFactory {
public Bar createInstance(String name) {
return new Bar(name);
}
}最後,我們向我們的 Bar Bean 定義中添加了一個 constructor-arg 元素:
<beans ...>
<bean id="instanceBarFactory"
class="com.baeldung.factorymethod.InstanceBarFactory" />
<bean id="bar"
factory-bean="instanceBarFactory"
factory-method="createInstance">
<constructor-arg value="someName" />
</bean>
</beans>我們可以以同樣的方式自動注入我們的 Bar Bean,就像我們為 Foo Bean 做的的那樣:
@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());
}
}3. 靜態工廠方法
我們還可以配置 Spring 使用靜態方法作為工廠方法。
雖然實例工廠方法應該首選,但這種技術在我們需要使用現有的、遺留的靜態方法來生成所需的 Bean 時非常有用。例如,如果一個工廠方法返回的是單例,我們可以配置 Spring 使用這個單例工廠方法。
類似於實例工廠方法,我們可以配置靜態方法,並可以指定參數。
3.1. 無參數
使用我們的 Foo 類作為所需 Bean,我們可以創建一個類, SingletonFooFactory,其中包含一個 createInstance 工廠方法,該方法返回 Foo 的單例實例:
public class SingletonFooFactory {
private static final Foo INSTANCE = new Foo();
public static Foo createInstance() {
return INSTANCE;
}
}本次,我們只需要創建 одном bean. 這個 Bean 只需要兩個屬性:
- class – 聲明我們的工廠類 (SingletonFooFactory)
- factory-method – 聲明靜態工廠方法 (createInstance)
將此應用到我們的 Spring XML 配置,我們得到:
<beans ...>
<bean id="foo"
class="com.baeldung.factorymethod.SingletonFooFactory"
factory-method="createInstance" />
</beans>最後,我們使用與之前相同的方式自動注入 Foo Bean:
@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. 使用參數
雖然我們應該儘可能避免更改靜態對象的狀態——比如我們的單例,但我們仍然可以向我們的靜態工廠方法傳遞參數。
要做到這一點,我們創建一個新的工廠方法,該方法接受我們想要的參數:
public class SingletonBarFactory {
private static final Bar INSTANCE = new Bar("unnamed");
public static Bar createInstance(String name) {
INSTANCE.setName(name);
return INSTANCE;
}
}之後,我們使用 constructor-arg 元素將所需的參數傳遞給 Spring:
<beans ...>
<bean id="bar"
class="com.baeldung.factorymethod.SingletonBarFactory"
factory-method="createInstance">
<constructor-arg value="someName" />
</bean>
</beans>最後,我們使用與之前相同的方式自動注入<em>Bar</em> Bean:
@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. 結論
在本文中,我們探討了如何配置 Spring 使用實例方法和靜態工廠方法,無論是否包含參數。
雖然通過構造函數和字段注入創建 Bean 更加常見,但工廠方法在處理複雜的創建步驟和遺留代碼時非常有用。