知識庫 / Spring RSS 訂閱

使用 Spring FactoryBean 的方法

Spring
HongKong
3
02:43 PM · Dec 06 ,2025

1. 概述

Spring Bean 容器中存在兩種類型的 Bean:普通 Bean 和工廠 Bean。Spring 直接使用普通 Bean,而工廠 Bean 則可以生成對象本身,這些對象由框架管理。

簡單來説,我們可以通過實現 org.springframework.beans.factory.FactoryBean 接口來構建工廠 Bean。

2. 工廠 Bean 的基本概念

2.1. 實現 FactoryBean 接口

讓我們首先看一下 FactoryBean 接口:

public interface FactoryBean {
    T getObject() throws Exception;
    Class<?> getObjectType();
    boolean isSingleton();
}

現在,我們來討論這三種方法:

  • getObject() – 返回由工廠產生的對象,並且這是 Spring 容器所使用的對象
  • getObjectType() – 返回此 FactoryBean 產生的對象的類型
  • isSingleton() – 表示由此 FactoryBean 產生的對象是否為單例

現在,我們來實施一個示例 FactoryBean。 我們將實現一個 ToolFactory,該工廠產生類型為 Tool 的對象。

public class Tool {

    private int id;

    // standard constructors, getters and setters
}

工具工廠本身:

public class ToolFactory implements FactoryBean<Tool> {

    private int factoryId;
    private int toolId;

    @Override
    public Tool getObject() throws Exception {
        return new Tool(toolId);
    }

    @Override
    public Class<?> getObjectType() {
        return Tool.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }

    // standard setters and getters
}

如我們所見,ToolFactory 是一個 FactoryBean,它可以產生 Tool 對象。

2.2. 使用 FactoryBean 進行基於 XML 的配置

現在我們來了解如何使用我們的 ToolFactory

我們將通過使用基於 XML 的配置來構建工具,該 XML 文件為 factorybean-spring-ctx.xml

<beans ...>

    <bean id="tool" class="com.baeldung.factorybean.ToolFactory">
        <property name="factoryId" value="9090"/>
        <property name="toolId" value="1"/>
    </bean>
</beans>

接下來,我們可以測試Tool對象是否正確注入:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:factorybean-spring-ctx.xml" })
public class FactoryBeanXmlConfigTest {
    @Autowired
    private Tool tool;

    @Test
    public void testConstructWorkerByXml() {
        assertThat(tool.getId(), equalTo(1));
    }
}

測試結果表明,我們成功地將由 ToolFactory 產生的工具對象,以及我們配置的屬性注入到其中。

測試結果還表明,Spring 容器使用由 FactoryBean 產生的對象,而不是自身進行依賴注入。

儘管 Spring 容器使用 FactoryBeangetObject() 方法的返回值作為 Bean,您也可以使用 FactoryBean 本身。

要訪問 FactoryBean,您只需要在 Bean 名稱前添加 "&" 即可。

讓我們嘗試獲取 factory bean 及其 factoryId 屬性:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:factorybean-spring-ctx.xml" })
public class FactoryBeanXmlConfigTest {

    @Resource(name = "&tool")
    private ToolFactory toolFactory;

    @Test
    public void testConstructWorkerByXml() {
        assertThat(toolFactory.getFactoryId(), equalTo(9090));
    }
}

2.3. 使用 FactoryBean 進行基於 Java 的配置

使用基於 Java 的配置時,FactoryBean 的使用方式與基於 XML 的配置有所不同,您需要顯式地調用 FactoryBeangetObject() 方法。

讓我們將上一節中提供的示例轉換為基於 Java 的配置示例:

@Configuration
public class FactoryBeanAppConfig {
 
    @Bean(name = "tool")
    public ToolFactory toolFactory() {
        ToolFactory factory = new ToolFactory();
        factory.setFactoryId(7070);
        factory.setToolId(2);
        return factory;
    }

    @Bean
    public Tool tool() throws Exception {
        return toolFactory().getObject();
    }
}

然後,我們測試Tool 對象是否已正確注入:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FactoryBeanAppConfig.class)
public class FactoryBeanJavaConfigTest {

    @Autowired
    private Tool tool;
 
    @Resource(name = "&tool")
    private ToolFactory toolFactory;

    @Test
    public void testConstructWorkerByJava() {
        assertThat(tool.getId(), equalTo(2));
        assertThat(toolFactory.getFactoryId(), equalTo(7070));
    }
}

測試結果表明,其效果與基於 XML 的先前配置測試相似。

3. 初始化方式

有時,在 <em style="font-style: italic;">FactoryBean</em> 被設置後,但在調用 <em style="font-style: italic;">getObject()</em> 方法之前,需要執行一些操作,例如屬性檢查。

可以通過實現 <em style="font-style: italic;">InitializingBean</em> 接口或使用 <em style="font-style: italic;">@PostConstruct</em> 註解來實現。

關於使用這兩種解決方案的更多詳細信息,請參閲另一篇文章:《在 Spring 中運行啓動時邏輯指南》

4. 抽象工廠Bean (AbstractFactoryBean)

Spring 提供 AbstractFactoryBean 作為 FactoryBean 實現的簡單模板超類。藉助該基類,我們可以更方便地實現一個工廠 Bean,該 Bean 創建單例對象或原型對象。

讓我們實現 SingleToolFactoryNonSingleToolFactory 以演示如何使用 AbstractFactoryBean 用於單例類型和原型類型:

public class SingleToolFactory extends AbstractFactoryBean<Tool> {

    private int factoryId;
    private int toolId;

    @Override
    public Class<?> getObjectType() {
        return Tool.class;
    }

    @Override
    protected Tool createInstance() throws Exception {
        return new Tool(toolId);
    }

    // standard setters and getters
}
<p>現在我們來介紹一下nonsingleton的實現:</p>
public class NonSingleToolFactory extends AbstractFactoryBean<Tool> {

    private int factoryId;
    private int toolId;

    public NonSingleToolFactory() {
        setSingleton(false);
    }

    @Override
    public Class<?> getObjectType() {
        return Tool.class;
    }

    @Override
    protected Tool createInstance() throws Exception {
        return new Tool(toolId);
    }

    // standard setters and getters
}

此外,這些工廠 bean 的 XML 配置如下:

<beans ...>

    <bean id="singleTool" class="com.baeldung.factorybean.SingleToolFactory">
        <property name="factoryId" value="3001"/>
        <property name="toolId" value="1"/>
    </bean>

    <bean id="nonSingleTool" class="com.baeldung.factorybean.NonSingleToolFactory">
        <property name="factoryId" value="3002"/>
        <property name="toolId" value="2"/>
    </bean>
</beans>

現在我們可以測試Worker對象屬性是否按預期注入:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:factorybean-abstract-spring-ctx.xml" })
public class AbstractFactoryBeanTest {

    @Resource(name = "singleTool")
    private Tool tool1;
 
    @Resource(name = "singleTool")
    private Tool tool2;
 
    @Resource(name = "nonSingleTool")
    private Tool tool3;
 
    @Resource(name = "nonSingleTool")
    private Tool tool4;

    @Test
    public void testSingleToolFactory() {
        assertThat(tool1.getId(), equalTo(1));
        assertTrue(tool1 == tool2);
    }

    @Test
    public void testNonSingleToolFactory() {
        assertThat(tool3.getId(), equalTo(2));
        assertThat(tool4.getId(), equalTo(2));
        assertTrue(tool3 != tool4);
    }
}

如測試結果所示,SingleToolFactory 生產單例對象,而 NonSingleToolFactory 生產原型對象。

請注意,SingleToolFactory 不需要設置單例屬性,因為在 AbstractFactory 中,單例屬性的默認值為 true

5. 結論

使用 FactoryBean 是一種良好的實踐,可以封裝複雜的構建邏輯或使配置高度可配置的對象變得更容易,尤其是在 Spring 中。

因此,在本文中,我們介紹瞭如何實現我們的 FactoryBean,如何在基於 XML 的配置和基於 Java 的配置中使用它,以及 FactoryBean 的一些其他方面,例如 FactoryBean 的初始化和 AbstractFactoryBean 的初始化。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.