Spring Bean Scopes: Guía para comprender los distintos scopes (ámbitos) de un Spring Bean


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:

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:

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:

Podemos hacer un test como el siguiente:

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:

El siguiente test muestra el comportamiento del prototype:

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:

Podemos definir los siguientes Beans:

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:

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

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

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.

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

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.

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

 

Dejar un Comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *