A logo showing the text blog.marcnuri.com
English
Inicio»Desarrollo Backend»Spring Bean Scopes: Guía para comprender los distintos scopes de un Spring Bean

Entradas Recientes

  • Eclipse JKube 1.19 está disponible!
  • Resumen de 2025: El Año de la IA
  • Synology DS224+: Cómo actualizar discos duros en RAID 1
  • Fabric8 Kubernetes Client 7.5 está disponible!
  • Impulsando Mi Productividad como Desarrollador con IA en 2025

Categorías

  • Antiguo
  • Cloud Native
  • Desarrollo Backend
  • Desarrollo Frontend
  • Herramientas
  • Ingeniería de calidad
  • Inteligencia Artificial
  • JavaScript
  • Operaciones
  • Personal
  • Proyectos personales
  • Reflexiones sobre Ingeniería

Archivos

  • febrero 2026
  • enero 2026
  • diciembre 2025
  • octubre 2025
  • septiembre 2025
  • julio 2025
  • mayo 2025
  • abril 2025
  • marzo 2025
  • febrero 2025
  • enero 2025
  • diciembre 2024
  • noviembre 2024
  • agosto 2024
  • junio 2024
  • mayo 2024
  • abril 2024
  • marzo 2024
  • febrero 2024
  • enero 2024
  • diciembre 2023
  • noviembre 2023
  • octubre 2023
  • septiembre 2023
  • agosto 2023
  • julio 2023
  • junio 2023
  • mayo 2023
  • abril 2023
  • marzo 2023
  • febrero 2023
  • enero 2023
  • diciembre 2022
  • noviembre 2022
  • octubre 2022
  • septiembre 2022
  • agosto 2022
  • julio 2022
  • junio 2022
  • mayo 2022
  • marzo 2022
  • febrero 2022
  • enero 2022
  • diciembre 2021
  • noviembre 2021
  • octubre 2021
  • septiembre 2021
  • agosto 2021
  • julio 2021
  • enero 2021
  • diciembre 2020
  • octubre 2020
  • septiembre 2020
  • agosto 2020
  • junio 2020
  • mayo 2020
  • marzo 2020
  • febrero 2020
  • enero 2020
  • noviembre 2019
  • septiembre 2019
  • julio 2019
  • diciembre 2018
  • agosto 2018
  • julio 2018
  • junio 2018
  • mayo 2018
  • marzo 2018
  • febrero 2018
  • noviembre 2017
  • octubre 2017
  • agosto 2017
  • julio 2017
  • enero 2017
  • julio 2016
  • enero 2016
  • diciembre 2015
  • noviembre 2015
  • diciembre 2014
  • noviembre 2014
  • octubre 2014
  • marzo 2014
  • febrero 2011
  • junio 2008
  • mayo 2008
  • abril 2008
  • enero 2008
  • junio 2007
  • mayo 2007
  • abril 2007
  • marzo 2007

Spring Bean Scopes: Guía para comprender los distintos scopes de un Spring Bean

2017-11-19 en Desarrollo Backend etiquetado Bean / Java / Ámbitos / Spring Framework / Spring Boot / Testing por Marc Nuri | Última actualización: 2026-02-16
English version

Introducción

En Spring Framework, la definición de un bean es esencialmente una receta para crear instancias de objetos. El scope (ámbito) determina cuándo y cómo Spring instancia estos objetos: ¿debería crear una instancia para toda la aplicación, o una nueva para cada petición?

Spring proporciona seis scopes para beans:

ScopeDuraciónCaso de uso
SingletonAplicaciónServicios stateless, recursos compartidos
PrototypePor petición al contenedorObjetos stateful, builders
RequestPetición HTTPDatos específicos de la petición
SessionSesión HTTPDatos de sesión de usuario
ApplicationServletContextEstado compartido a nivel de app
WebSocketSesión WebSocketDatos específicos de WebSocket

Los últimos cuatro (Request, Session, Application, WebSocket) requieren un ApplicationContext web-aware y no funcionarán en aplicaciones standalone.

Scope Singleton

Singleton es el scope por defecto. Spring crea exactamente una instancia del bean por contenedor y la cachea para todas las peticiones posteriores. Cada vez que solicitas este bean, obtienes el mismo objeto.

Puedes definir singletons usando anotaciones:

@Bean(name = SINGLETON_BEAN_SAMPLE_NAME)
public Sample sample() {
    return new Sample();
}

@Bean(name = SINGLETON_ANNOTATED_BEAN_SAMPLE_NAME)
@Scope(SCOPE_SINGLETON)
public Sample sampleAnnotated() {
    return new Sample();
}

Como Singleton es el valor por defecto, la anotación @Scope es opcional (primer ejemplo). Recomiendo añadirla explícitamente (segundo ejemplo) para que tu intención quede clara para otros desarrolladores.

El siguiente test demuestra el comportamiento singleton:

@Test
public void singletonTest_na_shouldBeSameInstance() {
    Sample singleton1 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_NAME, Sample.class);
    Sample singleton2 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_NAME, Sample.class);
    Assert.assertEquals(singleton1, singleton2);
    Sample singletonAnnotated1 = applicationContext.getBean(SINGLETON_ANNOTATED_BEAN_SAMPLE_NAME, Sample.class);
    Sample singletonAnnotated2 = applicationContext.getBean(SINGLETON_ANNOTATED_BEAN_SAMPLE_NAME, Sample.class);
    Assert.assertEquals(singletonAnnotated1, singletonAnnotated2);
    Assert.assertNotEquals(singleton1, singletonAnnotated1);
}

Las clases anotadas con @Component (o sus especializaciones como @Service, @Repository, @Controller) siguen las mismas reglas: singleton por defecto.

Aquí un ejemplo con @RestController:

SingletonScopedController.java
@RestController
public class SingletonScopedController extends AbstractController {
    /* ... */
    @GetMapping(AbstractController.SINGLETON_SCOPE_ENDPOINT)
    public String getUuid() {
        return super.getUuid();
    }
}

Este test usando MockMvc verifica el comportamiento singleton:

@Test
public void singletonScopedController_na_shouldReturnSameValues() throws Exception {
    String response1 = mockMvc.perform(get(AbstractController.SINGLETON_SCOPE_ENDPOINT))
        .andReturn().getResponse().getContentAsString();
    String response2 = mockMvc.perform(get(AbstractController.SINGLETON_SCOPE_ENDPOINT))
        .andReturn().getResponse().getContentAsString();
    Assert.assertEquals(response1, response2);
}

Scope Prototype

Con el scope Prototype, Spring crea una nueva instancia cada vez que solicitas el bean al contenedor. Esto es útil para objetos stateful que no deberían compartirse, como builders u objetos con estado mutable.

Advertencia

Spring no gestiona el ciclo de vida completo de los beans prototype. Una vez que el contenedor te entrega la instancia, tú eres responsable de la limpieza y liberación de recursos.

Define un bean prototype usando anotaciones:

@Bean(name = PROTOTYPE_BEAN_SAMPLE_NAME)
@Scope(SCOPE_PROTOTYPE)
public Sample samplePrototype() {
    return new Sample();
}

Este test muestra que cada petición devuelve una instancia diferente:

@Test
public void prototypeTest_na_shouldBeDifferentInstance() {
    Sample prototype1 = applicationContext.getBean(PROTOTYPE_BEAN_SAMPLE_NAME, Sample.class);
    Sample prototype2 = applicationContext.getBean(PROTOTYPE_BEAN_SAMPLE_NAME, Sample.class);
    Assert.assertNotEquals(prototype1, prototype2);
}

El problema del prototype en singleton

Un error común: cuando inyectas un bean Prototype en un Singleton, la inyección ocurre solo una vez (cuando se crea el Singleton). Esto significa que tu "prototype" efectivamente se comporta como un singleton.

Considera esta clase con una dependencia inyectada:

public class SampleAutowired {

    @Autowired
    @Qualifier(BEAN_NAME_FOR_AUTOWIRED_PROTOTYPE)
    private Sample sampleAutowiredPrototype;

    /* ... */
}

Con estas definiciones de beans:

@Bean(name = PROTOTYPE_BEAN_SAMPLE_NAME)
@Scope(SCOPE_PROTOTYPE)
@Qualifier(BEAN_NAME_FOR_AUTOWIRED_PROTOTYPE)
public Sample samplePrototype() {
    return new Sample();
}

@Bean(name = SINGLETON_BEAN_SAMPLE_AUTOWIRED_NAME)
@Scope(SCOPE_SINGLETON)
public SampleAutowired sampleAutowiredPrototype() {
    return new SampleAutowired();
}

El bean Sample tiene scope Prototype, pero como SampleAutowired es Singleton, Spring inyecta Sample solo una vez durante la instanciación. Cada petición posterior de SampleAutowired devuelve el mismo objeto con la misma instancia de Sample, anulando el propósito del scope Prototype.

El ejemplo PrototypeInSingleton demuestra esto:

@Test
public void singletonWithAutowiredPrototype_na_shouldBeInstance() {
    SampleAutowired singleton1 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_AUTOWIRED_NAME, SampleAutowired.class);
    SampleAutowired singleton2 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_AUTOWIRED_NAME, SampleAutowired.class);
    Assert.assertEquals(singleton1, singleton2);
    Assert.assertEquals(singleton1.getSampleAutowiredPrototype(), singleton2.getSampleAutowiredPrototype());
}

Consejo

Para soluciones a este problema usando ObjectProvider, @Lookup o scoped proxies, consulta mi publicación complementaria: Spring Bean Scopes: Singleton con Prototypes.

Scopes web-aware

Los scopes restantes (Request, Session, Application, WebSocket) solo funcionan en aplicaciones web con un WebApplicationContext.

Scope Request

Spring crea una nueva instancia para cada petición HTTP. El bean vive solo durante esa petición y se destruye cuando se envía la respuesta.

Define un bean con scope request:

// Beans basados en anotaciones @Bean
@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST)
public BeanSample beanSample() {
    return new BeanSample ();
}

// Beans basados en @Component
@RestController
@RequestScope
public class RequestScopedController extends AbstractController{

    @GetMapping(AbstractController.REQUEST_SCOPE_ENDPOINT)
    public String getUuid() {
            return super.getUuid();
    }

}

Cada petición obtiene una instancia nueva:

@Test
public void requestScopedController_na_shouldReturnDifferentValues() throws Exception {
    String response1 = mockMvc.perform(get(AbstractController.REQUEST_SCOPE_ENDPOINT))
        .andReturn().getResponse().getContentAsString();
    String response2 = mockMvc.perform(get(AbstractController.REQUEST_SCOPE_ENDPOINT))
        .andReturn().getResponse().getContentAsString();
    Assert.assertNotEquals(response1, response2);
}

Scope Session

Spring crea una instancia por sesión HTTP. Todas las peticiones dentro de la misma sesión comparten la misma instancia del bean, ideal para datos específicos del usuario como carritos de compra o preferencias.

// Beans basados en anotaciones @Bean
@Bean
@Scope(value = WebApplicationContext.SCOPE_SESSION)
public BeanSample beanSample() {
    return new BeanSample ();
}

// Beans basados en componentes
@RestController
@SessionScope
public class SessionScopedController extends AbstractController {

    @GetMapping(AbstractController.SESSION_SCOPE_ENDPOINT)
    public String getUuid() {
            return super.getUuid();
    }
}

Este test muestra que las peticiones de la misma sesión comparten instancia, pero sesiones diferentes obtienen instancias diferentes:

@Test
public void sessionScopedController_na_shouldReturnSameValues() throws Exception {
    MockHttpSession session1 = new MockHttpSession();
    MockHttpSession session2 = new MockHttpSession();
    String response1_1 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session1))
        .andReturn().getResponse().getContentAsString();
    String response1_2 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session1))
        .andReturn().getResponse().getContentAsString();
    Assert.assertEquals(response1_1, response1_2);
    String response2_1 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session2))
        .andReturn().getResponse().getContentAsString();
    String response2_2 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session2))
        .andReturn().getResponse().getContentAsString();
    Assert.assertEquals(response2_1, response2_2);
    Assert.assertNotEquals(response1_1, response2_1);
}

Scope Application

El scope Application crea una instancia por ServletContext. Esto es similar a Singleton, pero la distinción importa cuando múltiples aplicaciones basadas en servlets comparten el mismo ServletContext: compartirán la misma instancia del bean.

Para la mayoría de aplicaciones Spring Boot con un único ServletContext, el scope Application se comporta idénticamente a Singleton.

Scope WebSocket

Spring crea una instancia por sesión WebSocket. El bean vive durante la conexión WebSocket y se comparte entre todos los mensajes de esa sesión.

Scope Thread

Spring proporciona SimpleThreadScope para instancias de beans por hilo, pero no está registrado por defecto. Regístralo manualmente si lo necesitas:

@Configuration
public class ThreadScopeConfig {

  @Bean
  public static CustomScopeConfigurer customScopeConfigurer() {
    CustomScopeConfigurer configurer = new CustomScopeConfigurer();
    configurer.addScope("thread", new SimpleThreadScope());
    return configurer;
  }
}

Nota

Spring 5.0 eliminó el scope Global Session que se usaba para aplicaciones basadas en Portlets. Si trabajas con código legacy de Portlet, este scope ya no está disponible.

Preguntas frecuentes

¿Cuál es el scope por defecto en Spring?

Singleton. Si no especificas un scope, Spring crea una instancia por contenedor y la reutiliza para todas las peticiones.

¿Cuándo debería usar Prototype en lugar de Singleton?

Usa Prototype cuando cada consumidor necesita su propia instancia con estado independiente. Ejemplos comunes incluyen builders, objetos command, o cualquier bean que mantenga estado mutable que no debería compartirse.

¿Puedo inyectar un bean Prototype en un Singleton?

Sí, pero con un detalle: el Prototype se inyecta una vez cuando se crea el Singleton. Para un comportamiento Prototype real, usa ObjectProvider, @Lookup o scoped proxies. Consulta Spring Bean Scopes: Singleton con Prototypes para soluciones detalladas.

¿Cuál es la diferencia entre Singleton y el scope Application?

En la mayoría de apps Spring Boot, son idénticos. La diferencia aparece cuando múltiples aplicaciones basadas en servlets comparten el mismo ServletContext: los beans con scope Application se comparten entre todas ellas, mientras que los beans Singleton están limitados a un único contexto de aplicación.

Conclusión

Los scopes de beans de Spring controlan cuándo y cómo se instancian los objetos. El scope correcto depende de tu caso de uso:

ScopeUsa cuando...
SingletonEl bean es stateless o se puede compartir de forma segura (la mayoría de servicios)
PrototypeCada consumidor necesita estado independiente
RequestLos datos son específicos de una petición HTTP
SessionLos datos persisten entre peticiones del usuario (carrito, preferencias)
ApplicationSe comparte entre múltiples servlets en el mismo contexto
WebSocketLos datos persisten durante una conexión WebSocket

Para más información sobre patrones de inyección de dependencias y testing de componentes Spring, consulta mis otros tutoriales.

El código fuente completo con ejemplos ejecutables con JBang está disponible en GitHub.

Referencias

  • Spring Framework Reference - Bean Scopes
Ámbitos de una aplicación de Spring
Ámbitos de una aplicación de Spring
Twitter iconFacebook iconLinkedIn iconPinterest iconEmail icon

Comentarios en "Spring Bean Scopes: Guía para comprender los distintos scopes de un Spring Bean"

  • Avatar for Roberto
    Roberto
    2020-11-22 19:24
    Gracias, muy bien explicado

Navegador de artículos
Docker: Configurando un contenedor como un servicio de LinuxSpring-boot como un servicio en Linux
© 2007 - 2026 Marc Nuri