2009-05-06 6 views
22

Ich habe eine Singleton-Bean, die für jeden Aufruf einer Funktion einen Verweis auf eine andere (neue) Prototyp-Bean zurückgeben muss. Die einzige Möglichkeit, dies zu tun, besteht darin, programmatisch eine neue Prototyp-Bean-Instanz von BeanFactory/ApplicatioContext durch Aufrufen ihrer getBean() -Methode abzurufen. Codebeispiel wird folgen ...Spring Prototyp Bohnen in Kombination mit Singleton Bohnen und Dependency-Injektion. Gibt es einen Ansatz, der nur Konfiguration ist?

Gibt es einen besseren Weg, dies zu tun? Nur via Konfiguration, hoffentlich? (Persönlich bezweifle ich, es ist ...)

<bean id="protoBean" scope="prototype" 
     class="com.blahblah.ProtoBean" /> 

<bean id="singletonBean" 
     class="com.blahblah.SingletonBean" /> 

public class ProtoBean { 

    .... 
} 

public class SingletonBean { 

    private BeanFactory factory; 

    public ProtoBean dispense() { 
     return (ProtoBean) factory.getBean("protoBean"); 
    } 

    .... 
} 

Antwort

14

einen Blick auf Method Injection

+1

Diese Antwort ist veraltet, wie Christopher sagte, seit Frühjahr 3.0 ist das '' Element der richtige Weg dazu. Wie von shrini1000 gezeigt, macht die Methodeninjektion die Klasse unbeholfen zu testen. –

4

Unter Verwendung der Methode Injektion macht die Singleton-Bean-Klasse schwer zu Unit-Test (Sie müssen eine Unterklasse erstellen zu implementieren die Methode, die die Abhängigkeit ausgibt). Außerdem ist es weniger wiederverwendbar, da Sie es nicht direkt instantiieren können. Wenn Sie also nicht Spring verwenden und diese Klasse verwenden möchten, müssen Sie die Bean-Rückgabe-Methode unterklassen und bereitstellen.

Ein besserer Ansatz IMHO ist die Verwendung eines Proxy, einer Zielprototypenquelle und einer Zielbohne des Prototyps, wie folgt. Solch eine Singleton-Bean-Klasse ist leicht unit-testbar und besser wiederverwendbar.

<bean id="targetPooledObject" class="pool.PooledObject" scope="prototype"> 
    <constructor-arg value="42" /> 
</bean> 

<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource"> 
    <property name="targetBeanName" value="targetPooledObject" /> 
</bean> 

<bean id="pooledObject" class="org.springframework.aop.framework.ProxyFactoryBean"> 
    <property name="targetSource" ref="prototypeTargetSource" />   
</bean> 

<bean id="poolConsumer" class="pool.PoolConsumer"> 
    <property name="pooledObject" ref="pooledObject" /> 
</bean> 

Jetzt können wir pooledObject in eine Singleton Bohne injizieren (poolConsumer wie oben dargestellt) und für jeden Methodenaufruf, die wir auf dieser Singleton Bohne machen (zB jedes Mal, wenn wir poolConsumer.callPooledObjectMethod() die pooledObject.foo() wiederum nennen nennt) wir Hol dir eine neue PooledObject-Bean.

Es folgt der entsprechende Code:

public class PooledObject 
{ 
    private int x; 

    public PooledObject(int x) 
    { 
     this.x = x; 
    } 

    public void foo() 
    { 
     System.out.println("foo called"); 
    } 
} 

public class PoolConsumer 
{ 
    private PooledObject pooledObject; 

    public PooledObject getPooledObject() 
    { 
     return pooledObject; 
    } 

    public void setPooledObject(PooledObject pooledObject) 
    { 
     this.pooledObject = pooledObject; 
    } 

    public void callPooledObjectMethod() 
    { 
     pooledObject.foo(); 
    } 
} 
+1

interessant, aber könnten Sie bitte erarbeiten (d. H., Fügen Sie den Java-Code) – Yaneeve

+1

@ Yaneeve hinzugefügt den Code und modifizierte die Konfiguration ein wenig, um es zu reflektieren. Vergib meine Code-Unsauberkeit. – shrini1000

+0

@Yaneeve können wir auch einen Bohnenpool im Frühling erstellen. Verwenden Sie eine ähnliche Konfiguration wie oben, aber verwenden Sie 'CommonsPoolTargetSource' anstelle von 'PrototypeTargetSource'. Dies könnte z.B. bei der Verarbeitung von JMS-Nachrichten über Spring in einer MDB-Form. Im Folgenden finden Sie einige Details: http://StackOverflow.com/a/12668538/266103 – shrini1000

10

Von Frühling 3.0 können wir <aop:scoped-proxy> für Dependency Injection des richtigen Bereich verwenden. Hinter der Szene injiziert Spring proxied Objekte und ist verantwortlich für die Suche nach dem richtigen Bereich Kontext, sei es Prototyp, Sitzung oder Anfrage usw. Siehe die offiziellen Dokumentationen here.

Und um das Leben einfacher zu machen, hat Spring auch das proxyMode-Attribut für @Scope eingeführt, sodass wir nicht nur auf XML-Deklarationen beschränkt sind. Zum Beispiel:

@Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES) 

Stellen Sie sicher, eindeutig die injizierte Bohne zu dokumentieren ist ein Proxy für andere zu warnen, dass getClass() und Gießen kann das erwartete Ergebnis nicht ergeben. Stellen Sie außerdem sicher, dass equals() und hashCode() in der proxied-Klasse Zugriffsmethoden verwenden und nicht direkt auf Klassenvariablen zugreifen.