Как получить beans, созданный FactoryBean spring?
FactoryBean можно использовать для программного создания объектов, для которых может потребоваться сложная логика создания экземпляров.
Однако, кажется, что beans, созданный FactoryBean
, не становится spring. Правильно ли это толкование? Если да, есть ли какие-нибудь хорошие обходные пути? Пример короткого кода включен, чтобы проиллюстрировать мою проблему.
ApplicationContext:
<bean id="searcher" class="some.package.SearcherFactory" />
<bean id="service" class="some.package.Service" />
Factory реализация:
public class SearcherFactory implements FactoryBean<Searcher> {
@Override
public Searcher getObject() throws Exception {
return new Searcher(); // not so complex after all ;)
}
@Override
public Class<Searcher> getObjectType() {
return Searcher.class;
}
....
}
Класс, созданный factory:
public class Searcher() {
private Service service;
@Autowired
public void setService(Service service) {
// never invoked
this.service=service;
}
}
Ответы
Ответ 1
Объект, созданный FactoryBean
, управляется Spring, но не создается или не создается с помощью Spring. Используя FactoryBean
, вы сами несете ответственность за это. Все инъекции и конфиг должны обрабатываться с помощью FactoryBean
Существует альтернатива, которая может работать лучше для вас - используйте конфигурацию на основе аннотаций вместо конфигурации на основе XML. Это означает, что вы можете иметь сложную логику создания экземпляра в Java, в то время как все предметы, такие как @Autowired
, на самих объектах.
Я обычно использую конфигурацию стиля аннотаций для всех нетривиальных приложений Spring, это делает многое намного проще.
Ответ 2
Вот абстрактная реализация FactoryBean
, которая делает для вас аутсорсинг:
public abstract class AbstractAutowiringFactoryBean<T> extends
AbstractFactoryBean<T> implements ApplicationContextAware{
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(
final ApplicationContext applicationContext){
this.applicationContext = applicationContext;
}
@Override
protected final T createInstance() throws Exception{
final T instance = doCreateInstance();
if(instance != null){
applicationContext
.getAutowireCapableBeanFactory()
.autowireBean(instance);
}
return instance;
}
/**
* Create the bean instance.
*
* @see #createInstance()
*/
protected abstract T doCreateInstance();
}
Расширьте его, реализуйте методы getObjectType()
и doCreateInstance()
, и вы запускаете его с помощью autowiring.
Примечание. BeanPostProcessors не применяются, что потребует дополнительного кода.
Ответ 3
Как насчет этого?
<bean id="serviceFactory"
class="some.package.SearcherFactory" />
<bean id="service"
factory-bean="serviceFactory"
factory-method="getObject"/>
... а затем просто введите bean 'сервис и не заботитесь о factory в вашем коде
Ответ 4
Ручным способом будет:
- Ввести зависимости в factory bean
- установите их вручную на целевой объект.
Вы также можете ввести ApplicationContext
в factory bean (или получить его, выполнив ApplicationContextAware
) и сделать ctx.getAutowireCapableBeanFactory().autowireBean(bean)
Я признаю, что оба чувствуют себя странно.
Но на самом деле, если логика такая простая (только инстанцирование), используйте область prototype
.
Ответ 5
FactoryBean - это интерфейс, который вы, как разработчик, реализуете при написании классов factory и хотите, чтобы объект, созданный классом factory, управлялся как bean на Spring, а BeanFactory с другой стороны, представляет собой контейнер Spring IoC, он содержит управляемый beans и обеспечивает доступ к их восстановлению. Он является частью ядра структуры, которая реализует базовую функциональность инверсии контейнера управления.
В большинстве случаев вы не сможете напрямую использовать или реализовывать интерфейс BeanFactory, если только вы не расширяете основные функции фреймворка. Пока вы будете реализовывать FactoryBean, когда у вас есть объекты, созданные на фабриках, которым необходимо управлять с помощью Spring.
В более сжатых словах BeanFactory представляет контейнер Spring, в то время как FactoryBean представляет классы factory, чей созданный объект подбирается и регистрируется как bean в контейнере.
File: context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="sha" class="MessageDigestFactoryBean">
<property name="algorithm" value="SHA1"/>
</bean>
<bean id="md5" class="MessageDigestFactoryBean"/>
</beans>
File: Main.java
import java.security.MessageDigest;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class Main {
public static void main(String[] args) {
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("context.xml"));
String d1 = (String) factory.getBean("sha");
String d2 = (String) factory.getBean("md5");
System.out.println(d1);
System.out.println(d2);
}
}
class MessageDigestFactoryBean implements FactoryBean, InitializingBean {
private static final String DEFAULT_ALGORITHM = "MD5";
private String algorithm = DEFAULT_ALGORITHM;
public Object getObject() throws Exception {
return this.algorithm;
}
public Class getObjectType() {
return MessageDigest.class;
}
public boolean isSingleton() {
return true;
}
public void setAlgorithm(String algorithm) {
this.algorithm = algorithm;
}
public void afterPropertiesSet() throws Exception {
this.algorithm += " after setting";
}
}
Ответ 6
Нет, beans, созданный FactoryBean
, также управляется spring.