web-dev-qa-db-fra.com

Comment gérer les haricots créés par FactoryBean spring?

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;
      } 
}
30
Johan Sjöberg

Les objets créés par la variable FactoryBeansont 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.

19
skaffman

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.

27
Sean Patrick Floyd

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

18
Rostislav Matl

Une manière manuelle serait:

  1. Injecter les dépendances dans le bean factory
  2. définissez-les manuellement sur l'objet cible.

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.

4
Bozho

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";
  }
}
0
Kumar Abhishek