A logo showing the text blog.marcnuri.com
English
Inicio»Java»Spring Bean Scopes: Guía para comprender los distintos scopes (ámbitos) de un Spring Bean

Entradas Recientes

  • Fabric8 Kubernetes Client 7.2.0 está disponible!
  • Conectarse a un servidor MCP con JavaScript y AI SDK
  • Conectarse a un servidor MCP con JavaScript y LangChain.js
  • El Futuro de las Herramientas para Desarrolladores en la era de la IA
  • Conectarse a un servidor Model Context Protocol (MCP) con Java y LangChain4j

Categorías

  • Antiguo
  • Front-end
  • Go
  • Herramientas
  • Industria y negocios
  • Inteligencia Artificial
  • Java
  • JavaScript
  • Operaciones
  • Personal
  • Proyectos personales

Archivos

  • 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
  • agosto 2022
  • julio 2022
  • mayo 2022
  • marzo 2022
  • febrero 2022
  • enero 2022
  • diciembre 2021
  • noviembre 2021
  • octubre 2021
  • septiembre 2021
  • agosto 2021
  • julio 2021
  • diciembre 2020
  • octubre 2020
  • agosto 2020
  • junio 2020
  • mayo 2020
  • marzo 2020
  • febrero 2020
  • enero 2020
  • noviembre 2019
  • octubre 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
  • 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 (ámbitos) de un Spring Bean

2017-11-19 en Java etiquetado Bean / Java / Ámbitos / Spring Framework / Spring Boot / Testing por Marc Nuri | Última actualización: 2021-03-19
English version

Introducción

En este tutorial se muestran los distintos ámbitos (Scopes) para un Bean que podemos encontrar en Spring Framework.

Es prioritario entender que la definición de un Bean no es más que una receta para crear instancias de una clase de acuerdo con dicha definición. Partiendo de esta base, se puede asumir que la misma receta para crear instancias de una clase se puede emplear en múltiples ocasiones o varias veces durante el ciclo de vida de una aplicación.

Una de las características que permite Spring configurar en la definición de los Beans es el Scope o ámbito de los objetos creados a partir de dicha definición.

Spring por defecto incluye 7 Scopes diferentes:

  • Singleton
  • Prototype
  • Request
  • Session
  • Global session
  • Application
  • Websocket

De ellos, los 5 últimos sólo están disponibles en una aplicación web-aware.

Singleton

Singleton es el ámbito por defecto de un Bean. Este scope implica que el contenedor de Spring creará una única instancia compartida de la clase designada por este Bean, por lo que siempre que se solicite este Bean se estará inyectando el mismo objeto. La instancia se almacenará en un caché gestionado por Spring.

Podemos definir los singletons empleando anotaciones del siguiente modo:

1@Bean(name = SINGLETON_BEAN_SAMPLE_NAME)
2public Sample sample() {
3    return new Sample();
4}
5
6@Bean(name = SINGLETON_ANNOTATED_BEAN_SAMPLE_NAME)
7@Scope(SCOPE_SINGLETON)
8public Sample sampleAnnotated() {
9    return new Sample();
10}

Como se ha mencionado, Singleton es el ámbito por defecto, por lo que no es necesario definir su @Scope (primer ejemplo). No obstante, y para mayor claridad se puede emplear la anotación @Scope para subrayar este hecho.

El siguiente test muestra el comportamiento de los singletons:

1@Test
2public void singletonTest_na_shouldBeSameInstance() {
3    Sample singleton1 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_NAME, Sample.class);
4    Sample singleton2 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_NAME, Sample.class);
5    Assert.assertEquals(singleton1, singleton2);
6    Sample singletonAnnotated1 = applicationContext.getBean(SINGLETON_ANNOTATED_BEAN_SAMPLE_NAME, Sample.class);
7    Sample singletonAnnotated2 = applicationContext.getBean(SINGLETON_ANNOTATED_BEAN_SAMPLE_NAME, Sample.class);
8    Assert.assertEquals(singletonAnnotated1, singletonAnnotated2);
9    Assert.assertNotEquals(singleton1, singletonAnnotated1);
10}

Del mismo modo que para los beans regulares, cualquier clase anotada con @Component o cualquiera de sus extensiones se comportará como un Singleton a menos que se indique lo contrario.

Así, para un @RestController como el siguiente:

1@RestController
2public class SingletonScopedController extends AbstractController {
3    /* ... */
4    @GetMapping(AbstractController.SINGLETON_SCOPE_ENDPOINT)
5    public String getUuid() {
6        return super.getUuid();
7    }
8}

Podemos hacer un test como el siguiente:

1@Test
2public void singletonScopedController_na_shouldReturnSameValues() throws Exception {
3    String response1 = mockMvc.perform(get(AbstractController.SINGLETON_SCOPE_ENDPOINT))
4        .andReturn().getResponse().getContentAsString();
5    String response2 = mockMvc.perform(get(AbstractController.SINGLETON_SCOPE_ENDPOINT))
6        .andReturn().getResponse().getContentAsString();
7    Assert.assertEquals(response1, response2);
8}

Prototype

El ámbito Prototype implica que el contenedor de Spring creará una nueva instancia del objeto descrito por el Bean cada vez que se le haga una petición. Una de las reglas básicas que indica la documentación de Spring es que para entornos donde haya un mantenimiento de sesión (Stateful) se emplee el Prototype Scope y cuando no se mantenga la sesión del usuario (Stateless) se emplee el Singleton Scope, no obstante puede haber más casos de uso.

Es importante destacar que Spring no se “responsabiliza” de lo que ocurra con la instancia del objeto recibida. Es decir, el desarrollador es responsable de realizar las tareas de limpieza y liberación de recursos referenciados por el objeto definido por el Bean.

Del mismo modo que para los Singleton, podemos definir un Prototype con anotaciones del siguiente modo:

1@Bean(name = PROTOTYPE_BEAN_SAMPLE_NAME)
2@Scope(SCOPE_PROTOTYPE)
3public Sample samplePrototype() {
4    return new Sample();
5}

El siguiente test muestra el comportamiento del prototype:

1@Test
2public void prototypeTest_na_shouldBeDifferentInstance() {
3    Sample prototype1 = applicationContext.getBean(PROTOTYPE_BEAN_SAMPLE_NAME, Sample.class);
4    Sample prototype2 = applicationContext.getBean(PROTOTYPE_BEAN_SAMPLE_NAME, Sample.class);
5    Assert.assertNotEquals(prototype1, prototype2);
6}

Singleton Beans con Prototype Beans inyectados

Es muy importante resaltar que cuando se define un Bean de tipo Singleton que contiene dependencias a Prototype Beans, la inyección ocurre cuando el objeto singleton se instancia, por lo que la inyección ocurre una sola vez y por tanto el objeto inyectado por el Prototype Bean será siempre el mismo (a pesar de ser de ámbito Prototype).

Por ejemplo, para una clase con un Bean inyectado como la siguiente:

1public class SampleAutowired {
2
3    @Autowired
4    @Qualifier(BEAN_NAME_FOR_AUTOWIRED_PROTOTYPE)
5    private Sample sampleAutowiredPrototype;
6
7    /* ... */
8}

Podemos definir los siguientes Beans:

1@Bean(name = PROTOTYPE_BEAN_SAMPLE_NAME)
2@Scope(SCOPE_PROTOTYPE)
3@Qualifier(BEAN_NAME_FOR_AUTOWIRED_PROTOTYPE)
4public Sample samplePrototype() {
5    return new Sample();
6}
7
8@Bean(name = SINGLETON_BEAN_SAMPLE_AUTOWIRED_NAME)
9@Scope(SCOPE_SINGLETON)
10public SampleAutowired sampleAutowiredPrototype() {
11    return new SampleAutowired();
12}

El primer Bean (Sample) es de ámbito Prototype y se inyectará en el segundo (SampleAutowired) que es de ámbito Singleton. El comportamiento previsto es que siempre que recuperemos SampleAutowired la instancia de Sample será la misma, a pesar de ser un Prototype ya que éste se inyectó cuando se creó la instancia del Singleton.

El siguiente test demuestra esto:

1@Test
2public void singletonWithAutowiredPrototype_na_shouldBeInstance() {
3    SampleAutowired singleton1 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_AUTOWIRED_NAME, SampleAutowired.class);
4    SampleAutowired singleton2 = applicationContext.getBean(SINGLETON_BEAN_SAMPLE_AUTOWIRED_NAME, SampleAutowired.class);
5    Assert.assertEquals(singleton1, singleton2);
6    Assert.assertEquals(singleton1.getSampleAutowiredPrototype(), singleton2.getSampleAutowiredPrototype());
7}

Ámbitos de una aplicación web-aware

Tal como se ha mencionado, el resto de ámbitos sólo están disponibles en aplicaciones web-aware.

Request

El contenedor de Spring creará una nueva instancia del objeto definido por el Bean cada vez que reciba un HTTP request.

Para configurar un Bean con este ámbito podemos emplear las siguientes anotaciones en función de si se trata un Bean o un Componente:

1// Beans basados en anotaciones @Bean
2@Bean
3@Scope(value = WebApplicationContext.SCOPE_REQUEST)
4public BeanSample beanSample() {
5    return new BeanSample ();
6}
7
8// Beans basados en @Component
9@RestController
10@RequestScope
11public class RequestScopedController extends AbstractController{
12
13    @GetMapping(AbstractController.REQUEST_SCOPE_ENDPOINT)
14    public String getUuid() {
15            return super.getUuid();
16    }
17
18}

Para el @RestController anterior podemos demostrar su comportamiento con el siguiente test:

1@Test
2public void requestScopedController_na_shouldReturnDifferentValues() throws Exception {
3    String response1 = mockMvc.perform(get(AbstractController.REQUEST_SCOPE_ENDPOINT))
4        .andReturn().getResponse().getContentAsString();
5    String response2 = mockMvc.perform(get(AbstractController.REQUEST_SCOPE_ENDPOINT))
6        .andReturn().getResponse().getContentAsString();
7    Assert.assertNotEquals(response1, response2);
8}

Session

El contenedor de Spring creará una nueva instancia del objeto definido por el Bean para cada una de las sesiones HTTP (Aplicación Stateful) y entregará esa misma instancia cada vez que reciba una petición dentro de la misma sesión.

1// Beans basados en anotaciones @Bean
2@Bean
3@Scope(value = WebApplicationContext.SCOPE_SESSION)
4public BeanSample beanSample() {
5    return new BeanSample ();
6}
7
8// Beans basados en componentes
9@RestController
10@SessionScope
11public class SessionScopedController extends AbstractController {
12
13    @GetMapping(AbstractController.SESSION_SCOPE_ENDPOINT)
14    public String getUuid() {
15            return super.getUuid();
16    }
17}

Para el @RestController anterior podemos demostrar su comportamiento con el siguiente test:

1@Test
2public void sessionScopedController_na_shouldReturnSameValues() throws Exception {
3    MockHttpSession session1 = new MockHttpSession();
4    MockHttpSession session2 = new MockHttpSession();
5    String response1_1 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session1))
6        .andReturn().getResponse().getContentAsString();
7    String response1_2 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session1))
8        .andReturn().getResponse().getContentAsString();
9    Assert.assertEquals(response1_1, response1_2);
10    String response2_1 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session2))
11        .andReturn().getResponse().getContentAsString();
12    String response2_2 = mockMvc.perform(get(AbstractController.SESSION_SCOPE_ENDPOINT).session(session2))
13        .andReturn().getResponse().getContentAsString();
14    Assert.assertEquals(response2_1, response2_2);
15    Assert.assertNotEquals(response1_1, response2_1);
16}

Global Session

El ámbito Global Session tiene un comportamiento similar al de Session pero aplica solo en el contexto de aplicaciones web basadas en Portlets. La especificación de los Portlets define un tipo de sesión global que se comparte entre todos los portlets que componen una aplicación.

Si empleásemos este ámbito en una aplicación web normal, el comportamiento de los Beans sería el mismo que si se hubiesen definido con un scope de tipo Session.

1@Bean
2@Scope(value = WebApplicationContext.SCOPE_GLOBAL_SESSION)
3public BeanSample beanSample() {
4    return new BeanSample ();
5}

Application

El ámbito Application tiene un comportamiento similar al de Singleton ya que Spring creará una nueva instancia del Bean definido para cada ServletContext. Sin embargo, a diferencia del Singleton Scope, una aplicación de Spring puede contar con varios ServletContext con lo que la instancia que se devuelva para cada petición al Bean será la misma dentro de un contexto, pero diferente para cada uno de los ServletContext.

Websocket

El contenedor de Spring creará una nueva instancia del objeto definido por el Bean y la devolverá para cada petición que se produzca dentro del ciclo de vida de un Websocket.

Conclusión

Este post sirve como guía rápida para la comprensión de los distintos Scopes o ámbitos que existen dentro de una aplicación de Spring. Se incluyen ejemplos prácticos con JUnit tests donde se demuestran los diferentes comportamientos.

Todo el código que se muestra en la publicación está disponible en Github.

Referencias

https://docs.spring.io/spring/docs/4.3.12.RELEASE/spring-framework-reference/htmlsingle/#beans-factory-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 (ámbitos) 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 - 2025 Marc Nuri