如何使用 Spring FactoryBean?

1. 概述

Spring bean容器中有两种类型的bean:普通bean和工厂bean。前者Spring直接使用,后者可以自己生产对象,并由框架进行管理。

并且,简单地说,我们可以通过实现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生成的对象是否为单例。

2.2 实现一个示例FactoryBean

现在,让我们实现一个示例FactoryBean。我们将实现一个ToolFactory ,它生成Tool类型的对象:

public class Tool {

    private int id;

    // 标准构造函数, getters and setters略
}

ToolFactory本身:

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.3 使用基于 XML 配置的FactoryBean

现在让我们看看如何使用我们的ToolFactory。

我们将开始构建一个基于 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 生成的工具对象注入到了factorybean-spring-ctx.xml中配置的属性中。

测试结果还显示Spring容器使用FactoryBean生成的对象而不是其自身进行依赖注入。

尽管 Spring 容器使用FactoryBean的getObject()方法的返回值作为 Bean,但是您也可以使用FactoryBean本身。

要访问FactoryBean,您只需要在 bean 名称前添加“&”。

让我们尝试获取工厂 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.4. 使用基于 Java 的配置的FactoryBean

使用基于 Java 的配置的FactoryBean与基于 XML 的配置稍有不同,您必须明确调用FactoryBean的getObject()方法。

我们将上一小节中的示例转换为基于 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. 初始化方式

有时您需要在设置FactoryBean之后但在调用getObject()方法之前执行一些操作,例如属性检查。

您可以通过实现InitializingBean接口或使用@PostConstruct注释来实现这一点。

4.抽象工厂Bean

Spring 提供了AbstractFactoryBean作为FactoryBean实现的简单模板超类。有了这个基类,我们现在可以更方便地实现创建单例或原型对象的工厂 bean。

让我们实现一个SingleToolFactory和一个NonSingleToolFactory来展示如何将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
}

非单例实现:

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
}

5. 结论

使用FactoryBean是一种很好的做法,可以封装复杂的构造逻辑,或者使在 Spring 中配置高度可配置的对象更容易。

因此在本文中,我们介绍了如何实现FactoryBean的基础知识、如何在基于 XML 的配置和基于 Java 的配置中使用它,以及FactoryBean的一些其他方面,例如FactoryBean和AbstractFactoryBean的初始化。

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙殿殿主

你的打赏是我精心创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值