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;@Overridepublic Tool getObject() throws Exception {return new Tool(toolId);}@Overridepublic Class<?> getObjectType() {return Tool.class;}@Overridepublic 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 {@Autowiredprivate Tool tool;@Testpublic 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;@Testpublic 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;}@Beanpublic Tool tool() throws Exception {return toolFactory().getObject();}
}
然后,我们测试一下Tool对象是否被正确注入:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FactoryBeanAppConfig.class)
public class FactoryBeanJavaConfigTest {@Autowiredprivate Tool tool;@Resource(name = "&tool")private ToolFactory toolFactory;@Testpublic 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;@Overridepublic Class<?> getObjectType() {return Tool.class;}@Overrideprotected 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);}@Overridepublic Class<?> getObjectType() {return Tool.class;}@Overrideprotected Tool createInstance() throws Exception {return new Tool(toolId);}// standard setters and getters
}
5. 结论
使用FactoryBean是一种很好的做法,可以封装复杂的构造逻辑,或者使在 Spring 中配置高度可配置的对象更容易。
因此在本文中,我们介绍了如何实现FactoryBean的基础知识、如何在基于 XML 的配置和基于 Java 的配置中使用它,以及FactoryBean的一些其他方面,例如FactoryBean和AbstractFactoryBean的初始化。