Le FactoryBean peut être utilisé pour créer par programme des objets pouvant nécessiter une logique d'instanciation complexe.
Cependant, il semble que les haricots créés par le FactoryBean
ne deviennent pas gérés au printemps. Cette interprétation est-elle correcte? Si oui, existe-t-il des solutions de rechange pour Nice? Un exemple de code court est inclus pour illustrer mon problème.
ApplicationContext:
<bean id="searcher" class="some.package.SearcherFactory" />
<bean id="service" class="some.package.Service" />
Implantation d'usine:
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;
}
....
}
Classe créée par l'usine:
public class Searcher() {
private Service service;
@Autowired
public void setService(Service service) {
// never invoked
this.service=service;
}
}
Les objets créés par la variable FactoryBean
sont gérés par Spring, mais non instanciés ni configurés par Spring. En utilisant un FactoryBean
, vous en prenez la responsabilité vous-même. Toutes les injections et config doivent être traitées par la FactoryBean
Il existe une alternative qui pourrait mieux fonctionner pour vous - utilisez config basé sur des annotations au lieu de config . Cela signifie que vous pouvez avoir une logique d'instanciation complexe en Java, tout en utilisant des éléments tels que @Autowired
sur les objets eux-mêmes.
J'ai tendance à utiliser la configuration de style annotation pour toutes les applications Spring non triviales, cela facilite beaucoup de choses.
Voici une implémentation abstraite FactoryBean
qui effectue le câblage automatique pour vous:
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();
}
Étendez-le, implémentez les méthodes getObjectType()
et doCreateInstance()
et vous êtes opérationnel avec le câblage automatique.
Remarque: BeanPostProcessors ne sont pas appliqués, cela nécessiterait du code supplémentaire.
Et ça ?
<bean id="serviceFactory"
class="some.package.SearcherFactory" />
<bean id="service"
factory-bean="serviceFactory"
factory-method="getObject"/>
... et ensuite juste injecter le 'service' de haricot et ne vous souciez pas de l'usine dans votre code
Une manière manuelle serait:
Vous pouvez aussi injecter ApplicationContext
dans le bean factory (ou l'obtenir en implémentant ApplicationContextAware
), et ctx.getAutowireCapableBeanFactory().autowireBean(bean)
J'avoue que les deux se sentent étranges, cependant.
Mais en fait, si la logique est aussi simple (instanciation seulement), utilisez prototype
scope.
Un FactoryBean est une interface que vous implémentez en tant que développeur lors de l'écriture de classes d'usine et que vous voulez que l'objet créé par la classe de fabrique soit géré comme un bean par Spring, tandis que BeanFactory représente le conteneur Spring IoC il contient les beans gérés et permet de les récupérer. Il fait partie du noyau du framework qui implémente la fonctionnalité de base d'une inversion de conteneur de contrôle.
Dans la plupart des cas, vous n'utiliserez pas ou n'implémenterez pas directement l'interface BeanFactory, sauf si vous étendez les fonctionnalités principales du framework. Vous pouvez également implémenter le FactoryBean lorsque vous avez des objets créés par des usines qui doivent être gérés par Spring.
En termes plus succincts, BeanFactory représente le conteneur Spring, tandis que le FactoryBean représente les classes d'usine dont l'objet créé est capturé et enregistré en tant que bean dans le conteneur.
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";
}
}