Spring - 如何使用FactoryBean

原文地址:https://www.baeldung.com/spring-factorybean

1. 概述

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

简而言之,我们可以通过实现org.springframework.beans.factory.FactoryBean接口来构建工厂bean。

2. FactoryBean基础

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
}

ToolFactory本身:

public class ToolFactory implements FactoryBean {
 
    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的配置工具– factorybean-spring-ctx.xml:


 
    
        
        
    

接下来,我们可以测试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.3. 将FactoryBean与基于Java的配置一起使用

在基于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. AbstractFactoryBean

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

让我们实现一个SingleToolFactory和一个NonSingleToolFactory来演示如何将AbstractFactoryBean用于singleton和prototype类型:

public class SingleToolFactory extends AbstractFactoryBean {
 
    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 {
 
    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配置:


 
    
        
        
    
 
    
        
        
    

现在我们可以测试是否按预期注入了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中设置singleton属性,因为在AbstractFactory中,singleton属性的默认值为true。

5. 总结

使用FactoryBean是封装复杂的构造逻辑或使在Spring中更容易配置高度可配置对象的一种好习惯。

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

你可能感兴趣的:(Spring - 如何使用FactoryBean)