- 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 基于XML的配置
使用FactoryBean
现在来看看如何使用我们的ToolFactory
我们将基于
factorybean-spring-ctx.xml
开始构建一个Tool:
...>
id="tool" class="com.baeldung.factorybean.ToolFactory">
name="factoryId" value="9090"/>
name="toolId" value="1"/>
接下来,我们可以测试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生成的Tool对象注入到了
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基于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;
}
}
然后,我们测试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 注解来实现此目的。
有关使用这两个解决方案的更多详细信息,请参见另一篇文章: Spring中启动时运行逻辑指南。
4.
AbstractFactoryBean
Spring提供了AbstractFactoryBean作为
实现
FactoryBean的简单模板父类。使用这个基类,我们现在可以更方便地实现一个创建单例或原型对象的工厂bean。
我们实现一个SingleToolFactory和一个NonSingleToolFactory来显示如何使用AbstractFactoryBean来为单例和原型类型:
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配置:
...>
id="singleTool" class="com.baeldung.factorybean.SingleToolFactory">
name="factoryId" value="3001"/>
name="toolId" value="1"/>
id="nonSingleTool" class="com.baeldung.factorybean.NonSingleToolFactory">
name="factoryId" value="3002"/>
name="toolId" value="2"/>
现在我们可以测试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 结论
在Spring中使用FactoryBean可以很好地封装复杂的构造逻辑,或者使
配置
高度可配置的对象变得更加容易。
因此,在这篇文章中,我们介绍了如何实现我们自己的FactoryBean的
基础知识
,如何基于XML的配置和基于Java的配置使用
FactoryBean
,以及一些其他使用FactoryBean的地方,如初始化的FactoryBean和AbstractFactoryBean。
一如以往,该GitHub项目中提供了该示例的完整源代码。