2009-05-06 9 views
22

Tengo un bean singleton que necesita para cada llamada de una función devolver una referencia a un bean prototipo diferente (nuevo). La única forma en que puedo pensar en hacer esto es recuperar programáticamente una nueva instancia prototipo de bean de BeanFactory/ApplicatioContext invocando su método getBean(). La muestra del código seguirá ...Spring prototype beans en combinación con beans singleton e inyección de dependencia. ¿Hay un enfoque que solo sea de configuración?

¿Hay una mejor manera de hacer esto? Solo a través de la configuración, con suerte? (Personalmente, dudo que haya ...)

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

    .... 
} 

Respuesta

14

echar un vistazo a Method Injection

+1

Esta respuesta no está actualizada, como dijo Christopher, desde Spring 3.0 el elemento '' es la forma correcta de hacerlo. Además, como señaló shrini1000, la inyección de método hace que la clase sea torpe para probar. –

4

El uso de la inyección método hace que la clase Singleton-frijol difícil unidad de prueba (es necesario crear una subclase de implementar el método que da la dependencia). Además, es menos reutilizable porque no puedes instanciarlo directamente, por lo que si no estás usando Spring y quieres utilizar esta clase, deberás crear una subclase y proporcionar el método de devolución de beans.

Un mejor enfoque en mi humilde opinión es utilizar un proxy, un prototipo de fuente de destino y un prototipo de frijol objetivo, de la siguiente manera. Tal clase de singleton-bean es fácilmente comprobable por unidad y mejor reutilizable.

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

Ahora podemos inyectar pooledObject en un grano de Singleton (poolConsumer como se muestra arriba), y para cada llamada al método que hacemos en ese grano de Singleton, (por ejemplo, cada vez que llamamos a poolConsumer.callPooledObjectMethod() que a su vez llama a pooledObject.foo()) nos obtener un nuevo bean PooledObject.

A continuación se presenta el código correspondiente:

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

interesante, pero podría por favor elaborar (es decir, agregar el código de Java) – Yaneeve

+1

@Yaneeve agregó el código y modificó la configuración un poco para reflejarlo. Perdona mi código: suciedad. – shrini1000

+0

@Yaneeve también podemos crear un grupo de frijoles en primavera. Use una configuración similar a la anterior, pero use 'CommonsPoolTargetSource' en lugar de 'PrototypeTargetSource'. Esto podría ser útil, p. al procesar mensajes JMS a través de Spring de forma MDB. Los siguientes son algunos detalles: http://stackoverflow.com/a/12668538/266103 – shrini1000

10

De Spring 3.0, podemos utilizar <aop:scoped-proxy> para la inyección de dependencias del alcance adecuado. Detrás de la escena, Spring inyecta objetos con proxy y es responsable de encontrar el contexto de alcance correcto, ya sea un prototipo, sesión o solicitud, etc. Consulte la documentación oficial here.

Y para hacer la vida más fácil, Spring también ha introducido el atributo proxyMode para @Scope, por lo que no estamos limitados a declaraciones XML solamente. Por ejemplo:

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

Asegúrese de documentar claramente el frijol inyectado es un proxy para advertir a otros que getClass() y la fundición riesgo de no producir el resultado esperado. Además, asegúrese de que equals() y hashCode() en la clase proxy utilizan métodos de acceso en lugar de acceder directamente a las variables de clase.

Cuestiones relacionadas