1. Обзор
В контейнере для бобов Spring есть два вида бобов: обычные бобы и заводские бобы. Spring использует первое напрямую, тогда как второе может создавать объекты самостоятельно, которыми управляет фреймворк.
И, проще говоря, мы можем построить фабричный компонент, реализовав интерфейс org.springframework.beans.factory.FactoryBean
.
2. Основы фабричных бобов ``
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<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.2. Используйте FactoryBean
с конфигурацией на основе XML
Давайте теперь посмотрим, как использовать нашу ToolFactory
.
Мы начнем создавать инструмент с конфигурацией на основе XML — factorybean-spring-ctx.xml
:
<beans ...>
<bean id="tool" class="com.foreach.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 использует возвращаемое значение метода getObject
() FactoryBean
в качестве компонента, вы также можете использовать сам FactoryBean
.
Чтобы получить доступ к FactoryBean
, вам просто нужно добавить «&» перед именем компонента.
Давайте попробуем получить фабричный компонент и его свойство 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
Использование FactoryBean
с конфигурацией на основе Java немного отличается от конфигурации на основе XML, вы должны явно вызывать метод getObject
()
FactoryBean .
Преобразуем пример из предыдущего подраздела в пример конфигурации на основе 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
.
Подробнее об использовании этих двух решений рассказано в другой статье: Guide To Running Logic on Startup in Spring .
4. Абстрактная ФабрикаБин
Spring предоставляет AbstractFactoryBean
как простой суперкласс шаблона для реализаций FactoryBean
. С помощью этого базового класса мы теперь можем более удобно реализовать фабричный компонент, который создает объект-одиночку или объект-прототип.
Давайте реализуем 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
}
Кроме того, конфигурация XML для этих заводских компонентов:
<beans ...>
<bean id="singleTool" class="com.foreach.factorybean.SingleToolFactory">
<property name="factoryId" value="3001"/>
<property name="toolId" value="1"/>
</bean>
<bean id="nonSingleTool" class="com.foreach.factorybean.NonSingleToolFactory">
<property name="factoryId" value="3002"/>
<property name="toolId" value="2"/>
</bean>
</beans>
Теперь мы можем проверить, внедряются ли свойства объектов 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
создает объект singleton, а NonSingleToolFactory
создает объект-прототип.
Обратите внимание, что нет необходимости устанавливать свойство singleton в SingleToolFactory
, потому что в AbstractFactory
значение свойства singleton по умолчанию равно true
.
5. Вывод
Использование FactoryBean
может быть хорошей практикой для инкапсуляции сложной логики построения или упрощения настройки объектов с широкими возможностями настройки в Spring.
Итак, в этой статье мы представили основы того, как реализовать наш FactoryBean
, как использовать его как в конфигурации на основе XML, так и в конфигурации на основе Java, а также некоторые другие различные аспекты FactoryBean
, такие как инициализация FactoryBean
и AbstractFactoryBean
.
Как всегда, полный исходник закончился на GitHub .