Перейти к основному содержимому

Как использовать Spring FactoryBean?

· 5 мин. чтения

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 .