Spring Singleton vs Prototype es una de las preguntas clásicas de Spring Framework y a muchas personas les surgen dudas sobre como funciona el scope o ámbito de los beans que generamos. Vamos a intentar aclararlo con un ejemplo sencillo. Supongamos que tenemos la siguiente clase de servicio con su interface.
package com.arquitecturajava; public interface ServicioTareas { public void lanzar(); } package com.arquitecturajava; public class ServicioTareasImpl implements ServicioTareas{ public void lanzar() { System.out.println("Tarea1"); System.out.println("Tarea2"); System.out.println("Tarea3"); } }
Se trata de una sencilla clase que ejecuta tres tareas. La vamos a dar de alta en el application-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"> <beans> <bean id="tareas" class="com.arquitecturajava.ServicioTareasImpl" /> </beans> </beans>
Hecho esto podemos en un programa main instanciar la clase de servicio con Spring Framework y ejecutarla.
package com.arquitecturajava; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Principal { public static void main(String[] args) { ApplicationContext contexto = new ClassPathXmlApplicationContext( "/application-context.xml"); ServicioTareas tareas = contexto.getBean(ServicioTareas.class); tareas.lanzar(); } }
El programa se ejecutará y mostrará por pantalla las distintas tareas:
Sin embargo es evidente que se trata de una situación poco elegante. Sería mucho mas flexible diseñarlo con dos conceptos ServicioTareas y Tarea.
Vamos a ver como quedarían las clases y los interfaces:
package com.arquitecturajava; public interface ServicioTareas2 { public void lanzar(); public void add(Tarea t); }
package com.arquitecturajava; import java.util.ArrayList; import java.util.List; public class ServicioTareas2Impl implements ServicioTareas2{ private List<Tarea> lista= new ArrayList<Tarea>(); public void lanzar() { for(Tarea t:lista) { t.ejecutar(); } } public void add(Tarea t) { lista.add(t); } }
Como vemos el código es sencillo a nivel del Servicio ya que ahora almacenamos un ArrayList de tareas para mejorar la flexibilidad. Nos queda por ver el código fuente de la Tarea:
package com.arquitecturajava; public interface Tarea { public void setMensaje(String mensaje); public String getMensaje(); public void ejecutar(); }
package com.arquitecturajava; public class TareaImpl implements Tarea { private String mensaje; public void ejecutar() { System.out.println(mensaje); } public String getMensaje() { return mensaje; } public void setMensaje(String mensaje) { this.mensaje = mensaje; }
Realizadas estas operaciones simplemente tenemos que dar de alta los beans en el application-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"> <beans > <bean id="tareas" class="com.arquitecturajava.ServicioTareasImpl"> </bean> <bean id="tarea" class="com.arquitecturajava.TareaImpl"/> </beans> </beans>
Es momento de instanciar las clases desde un programa y ejecutar el código:
package com.arquitecturajava; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Principal02 { public static void main(String[] args) { ApplicationContext contexto = new ClassPathXmlApplicationContext( "/application-context.xml"); ServicioTareas2 tareas = contexto .getBean(ServicioTareas2.class); Tarea t1= contexto.getBean(Tarea.class); t1.setMensaje("tarea1"); Tarea t2= contexto.getBean(Tarea.class); t2.setMensaje("tarea2"); Tarea t3= contexto.getBean(Tarea.class); t3.setMensaje("tarea3"); tareas.add(t1); tareas.add(t2); tareas.add(t3); tareas.lanzar(); } } }
El resultado será el siguiente:
Spring Singleton
Lamentablemente no es el que queríamos y tenemos un problema. Esto se debe a que todos nuestros beans son “singleton” es decir solo existe una única instancia de ellos .
Por lo tanto solo tenemos un objeto tarea que se ejecuta con el último texto. Para solventar estos temas tenemos que modificar el ámbito (scope) de la Tarea y convertirla en “prototype” de esta manera cada vez que pidamos un objeto tarea a Spring se creará un nuevo bean.
El código del application-context.xml será el siguiente:
<?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"> <beans > <bean id="tareas" class="com.arquitecturajava.ServicioTareas2Impl" scope="prototype"> </bean> <bean id="tarea" class="com.arquitecturajava.TareaImpl" scope="prototype"/> </beans> </beans>
Ahora el resultado será el correcto:
La mayor parte de las veces usaremos el scope de Singleton que es el scope por defecto. Sin embargo en otras situaciones podemos querer usar el scope prototype que nos asegura que se generará una nueva instancia cada vez que solicitemos el bean a Spring. El scope de prototype es habitual cuando deseamos mantener estado.
Otros artículos relacionados:
Hola emilio,habra un blog donde se explique cuando se detalla un bean con scope =”session”, gracias.
Hola Cecilio, gracias por el aporte, me fue útil, pero me quedo una duda.
Es necesario definir el scope prototype en ambas implementaciones? o es suficiente con definir el scope solo en TareaImpl ya que es donde agrega nuevas tareas?
Saludos,
Juan
[…] artículos relacionados: Spring Singleton vs Prototype , Spring Bean […]
[…] artículos relacionados: Singleton ClassLoaders ,Spring Singleton vs Prototype , EJB […]
Hola!! Tengo 3 dudas,
1)todas las clases con las anotaciones @controller, @repository, @service, @component o los beans que son gestionados por application-context son por defecto singleton, estoy en lo correcto?
2) cuando usamos hibernate o jpa las entidades que son mapeadas @entity, esas ya dejan de ser singleton? Esas ya no son gestionadas por spring?
3) En dado caso tendríamos nosotros que implementarlas con el patrón singleton Si quisiéramos tener una sola instancia?
Muchas Gracias!!!!
Las clases anotadas como @controller, @Service o @Component son gestionadas por Spring Container (una implementación del container es spring ApplicationContext), @Repository o @Entity son gestionadas por JPA Container.
Respecto a tener una sóla instancia de @Entity no tiene sentido.