Spring 中有两类bean:普通bean(ordinary bean)和工厂bean(factory bean)。要实现一个工厂bean,只要类实现接口 org.springframework.beans.factory.FactoryBean
即可1 2。
FactoryBean
接口的定义如下:
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
}
这是个泛型接口,泛型 T
指明了这个工厂bean产生的对象的类型。
先定义一个普通的类,即工厂bean要产生的bean类型:
public class Tool {
private int id;
// standard constructors, getters and setters
}
再来一个实现 FactoryBean
接口的类:
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
}
接下来装配这个工厂类。
用基于XML的配置来装配这个工厂bean:
<beans ...>
<bean id="tool" class="com.baeldung.factorybean.ToolFactory">
<property name="factoryId" value="9090"/>
<property name="toolId" value="1"/>
</bean>
</beans>
最后测试下,发现容器可以成功注入我们想要的Tool类的bean了:
@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));
}
}
也就是说: 容器是用 FactoryBean
的 getObject()
方法产生的bean来进行依赖注入,而不是工厂 bean 本身。
也可以直接使用工厂 bean,这需要在bean的名称前加&
,如下所示:
@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));
}
}
基于Java的配置来装配工厂 bean,与基于xml的有所不同。除了配置工厂 bean外,还需要配置 FactoryBean
的 getObject()
返回的bean,如下所示:
@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();
}
}
然後测试下是否能够正确注入(注:在注入时,实际产生的bean,只用 @Autowired
注解即可;而工厂bean则仍需要在 @Resource
注解中通过 name
属性带上 &
来指明):
@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的配置方式类似的效果。
How to use the Spring FactoryBean? ↩︎
如何使用Spring FactoryBean? ↩︎