2009-05-06 11 views
22

Ho un bean singleton che necessita di ogni chiamata di una funzione per restituire un riferimento a un bean di prototipo diverso (nuovo). L'unico modo in cui posso pensare di farlo è quello di recuperare a livello di codice una nuova istanza di bean prototipo da BeanFactory/ApplicatioContext invocando il suo metodo getBean(). Codice di esempio seguirà ...Fagioli prototipo di primavera in combinazione con fagioli singleton e iniezione di dipendenza. C'è un approccio che è solo la configurazione?

C'è un modo migliore per fare questo? Solo tramite configurazione, si spera? (Personalmente, dubito che ci sia ...)

<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"); 
    } 

    .... 
} 

risposta

14

un'occhiata a Method Injection

+1

Questa risposta è obsoleta, come ha detto Christopher, poiché Spring 3.0 l'elemento '' è il modo giusto per farlo. Inoltre, come sottolineato da shrini1000, l'iniezione di metodi rende la classe goffa da testare. –

4

Usando il metodo di iniezione rende la classe Singleton-bean difficile unità-test (è necessario creare una sottoclasse di implementare il metodo che dà la dipendenza). Inoltre è meno riutilizzabile perché non è possibile creare un'istanza immediata, quindi se non si utilizza Spring e si desidera utilizzare questa classe, sarà necessario creare una sottoclasse e fornire il metodo di restituzione dei bean.

Un approccio migliore IMHO consiste nell'utilizzare un proxy, un prototipo di origine di destinazione e un prototipo di bean di destinazione, come segue. Tale classe di bean singleton è facilmente testata da unità e riutilizzabile meglio.

<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> 

Ora si può iniettare pooledObject in un bean singleton (poolConsumer come mostrato sopra), e per ogni chiamata di metodo che facciamo su tale fagiolo singoletto, (ad esempio, ogni volta che chiamiamo poolConsumer.callPooledObjectMethod() che a sua volta richiama pooledObject.foo()) abbiamo ottenere un nuovo bean PooledObject.

seguito è il codice corrispondente:

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

interessante, ma potreste per favore elaborare (ad esempio, aggiungere il codice java) – Yaneeve

+1

@Yaneeve ha aggiunto il codice e modificato leggermente la configurazione per rifletterlo. Perdona il mio codice: impurità. – shrini1000

+0

@Yaneeve possiamo anche creare un pool di bean in primavera. Usa una configurazione simile come sopra, ma usa "CommonsPoolTargetSource" invece di "PrototypeTargetSource". Questo potrebbe essere utile per es. durante l'elaborazione di messaggi JMS tramite Spring in modo MDB. Di seguito sono riportati alcuni dettagli: http://stackoverflow.com/a/12668538/266103 – shrini1000

10

Da Spring 3.0, possiamo usare <aop:scoped-proxy> per iniezione di dipendenza della corretta applicazione. Dietro la scena, Spring inietta gli oggetti con proxy e ha la responsabilità di trovare il giusto contesto di ambito, che si tratti di prototipo, sessione o richiesta, ecc. Vedi i documenti ufficiali here.

E per semplificare la vita, Spring ha anche introdotto l'attributo proxyMode per @Scope, quindi non siamo limitati solo alle dichiarazioni XML. Per esempio:

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

Assicurarsi di documentare in modo chiaro il fagiolo iniettata è un proxy per avvertire gli altri che getClass() e la colata non può produrre il risultato atteso. Inoltre, assicurarsi che equals() e hashCode() nella classe proxy utilizzino i metodi di accesso piuttosto che accedere direttamente alle variabili di classe.

Problemi correlati