wake-up-neo.net

Wie lassen sich Bohnen von FactoryBean spring verwalten?

Mit FactoryBean können Objekte programmiert erstellt werden, für die eine komplexe Instantiierungslogik erforderlich ist. 

Es scheint jedoch, dass die Beans erstellt von der FactoryBean nicht im Frühling verwaltet werden. Ist diese Interpretation korrekt? Wenn ja, gibt es Nizza Workarounds? Ein kurzer Codebeispiel ist enthalten, um mein Problem zu veranschaulichen. 

Anwendungskontext:

<bean id="searcher" class="some.package.SearcherFactory" /> 
<bean id="service" class="some.package.Service" /> 

Werksumsetzung:

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;
    }
    .... 
}

Von der Fabrik erstellte Klasse: 

public class Searcher() {
      private Service service;

      @Autowired
      public void setService(Service service) {
           // never invoked
           this.service=service;
      } 
}
30
Johan Sjöberg

Das von FactoryBeanare erstellte Objekt wird von Spring verwaltet, jedoch nicht instanziiert oder von Spring konfiguriert. Durch die Verwendung einer FactoryBean übernehmen Sie selbst die Verantwortung dafür. Alle Injektionen und Konfigurationen müssen vom FactoryBean verarbeitet werden.

Es gibt eine Alternative, die für Sie möglicherweise besser geeignet ist - verwenden Sie annotation-based config anstelle von XML-based config . Dies bedeutet, dass Sie in Java über eine komplexe Instantiierungslogik verfügen können, während Sie Objekte wie @Autowired für die Objekte selbst verwenden.

Ich neige dazu, Annotation-Style-Konfigurationen für alle nicht-trivialen Spring-Apps zu verwenden. Dadurch werden viele Dinge wesentlich einfacher.

19
skaffman

Hier ist eine abstrakte FactoryBean-Implementierung, die für Sie automatisch verwendet wird:

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();

}

Erweitern Sie es, implementieren Sie die Methoden getObjectType() und doCreateInstance().

Hinweis: BeanPostProcessors werden nicht angewendet, dies würde zusätzlichen Code erfordern.

27

Was ist damit?

<bean id="serviceFactory"
      class="some.package.SearcherFactory" />


<bean id="service"
      factory-bean="serviceFactory"
      factory-method="getObject"/>

... und dann geben Sie einfach den Bean-Service ein und kümmern sich nicht um die Fabrik in Ihrem Code

18
Rostislav Matl

Ein manueller Weg wäre:

  1. Fügen Sie die Abhängigkeiten in die Factory-Bean ein
  2. setzen Sie sie manuell auf das Zielobjekt.

Sie können auch ApplicationContext in das Factory-Bean einfügen (oder durch Implementierung von ApplicationContextAware erhalten) und ctx.getAutowireCapableBeanFactory().autowireBean(bean) ausführen.

Ich gebe zu, dass sich beide seltsam fühlen.

Wenn die Logik jedoch so einfach ist (nur Instantiierung), verwenden Sie prototype scope.

4
Bozho

FactoryBean ist eine Schnittstelle, die Sie als Entwickler beim Schreiben von Factory-Klassen implementieren. Sie möchten, dass das von der Factory-Klasse erstellte Objekt von Spring als Bean verwaltet wird, während BeanFactory dagegen den Spring-IoC-Container darstellt. Es enthält die verwalteten Beans und ermöglicht den Zugriff auf diese. Es ist Teil des Kerns des Frameworks, das die Basisfunktionalität einer Invertierung des Steuerungscontainers implementiert.

In den meisten Fällen können Sie die BeanFactory-Schnittstelle nicht direkt verwenden oder implementieren, es sei denn, Sie erweitern die Kernfunktionalität des Frameworks. Sie würden FactoryBean zwar implementieren, wenn Sie über Objekte verfügen, die von Factories erstellt werden, die von Spring verwaltet werden müssen.

Kurz gesagt, repräsentiert die BeanFactory den Spring-Container, während die FactoryBean Factory-Klassen darstellt, deren erzeugtes Objekt als Bean in dem Container aufgenommen und registriert wird.

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