Guía rápida de los visores Spring Bean

1. Información general

En este tutorial rápido, aprenderá sobre los diferentes tipos de ámbitos de frijol en el marco de Spring.

El alcance de un bean define el ciclo de vida y la visibilidad de ese bean en los contextos en los que se utiliza.

La última versión del marco Spring define 6 tipos de ámbitos:

  • único
  • prototipo
  • solicitud
  • sesión
  • solicitud
  • websocket

Los últimos cuatro ámbitos mencionados solicitud, sesión, aplicación y websocket solo están disponibles en una aplicación compatible con web.

2. Alcance Singleton

Definir un bean con alcance singleton significa que el contenedor crea una única instancia de ese bean, y todas las solicitudes para ese nombre de bean devolverán el mismo objeto, que se almacena en caché. Cualquier modificación al objeto se reflejará en todas las referencias al bean. Este ámbito es el valor predeterminado si no se especifica ningún otro ámbito.

Creemos una entidad Person para ejemplificar el concepto de ámbitos:

public class Person { private String name; // standard constructor, getters and setters }

Luego, definimos el bean con alcance singleton usando la anotación @Scope :

@Bean @Scope("singleton") public Person personSingleton() { return new Person(); }

También podemos usar una constante en lugar del valor de Cadena de la siguiente manera:

@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)

Ahora procedemos a escribir una prueba que muestra que dos objetos que hacen referencia al mismo bean tendrán los mismos valores, incluso si solo uno de ellos cambia su estado, ya que ambos hacen referencia a la misma instancia de bean:

private static final String NAME = "John Smith"; @Test public void givenSingletonScope_whenSetName_thenEqualNames() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("scopes.xml"); Person personSingletonA = (Person) applicationContext.getBean("personSingleton"); Person personSingletonB = (Person) applicationContext.getBean("personSingleton"); personSingletonA.setName(NAME); Assert.assertEquals(NAME, personSingletonB.getName()); ((AbstractApplicationContext) applicationContext).close(); }

El archivo scopes.xml de este ejemplo debe contener las definiciones xml de los beans utilizados:

3. Alcance del prototipo

Un bean con alcance de prototipo devolverá una instancia diferente cada vez que se solicite desde el contenedor. Se define estableciendo el prototipo de valor en la anotación @Scope en la definición de bean:

@Bean @Scope("prototype") public Person personPrototype() { return new Person(); }

También podríamos usar una constante como lo hicimos para el alcance singleton:

@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)

Ahora escribiremos una prueba similar a la anterior que muestra que dos objetos que solicitan el mismo nombre de bean con el prototipo de alcance tendrán diferentes estados, ya que ya no se refieren a la misma instancia de bean:

private static final String NAME = "John Smith"; private static final String NAME_OTHER = "Anna Jones"; @Test public void givenPrototypeScope_whenSetNames_thenDifferentNames() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("scopes.xml"); Person personPrototypeA = (Person) applicationContext.getBean("personPrototype"); Person personPrototypeB = (Person) applicationContext.getBean("personPrototype"); personPrototypeA.setName(NAME); personPrototypeB.setName(NAME_OTHER); Assert.assertEquals(NAME, personPrototypeA.getName()); Assert.assertEquals(NAME_OTHER, personPrototypeB.getName()); ((AbstractApplicationContext) applicationContext).close(); } 

El archivo scopes.xml es similar al presentado en la sección anterior mientras agrega la definición xml para el bean con alcance prototipo :

4. Ámbitos compatibles con la Web

Como se mencionó, hay cuatro ámbitos adicionales que solo están disponibles en un contexto de aplicación compatible con web. Estos se utilizan con menos frecuencia en la práctica.

El alcance de la solicitud crea una instancia de bean para una sola solicitud HTTP, mientras que el alcance de la sesión crea una sesión HTTP.

El ámbito de la aplicación crea la instancia de bean para el ciclo de vida de un ServletContext y el ámbito de websocket lo crea para una sesión particular de WebSocket .

Creemos una clase para usar para instanciar los beans:

public class HelloMessageGenerator { private String message; // standard getter and setter }

4.1. Alcance de la solicitud

Podemos definir el bean con el alcance de la solicitud usando la anotación @Scope :

@Bean @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator requestScopedBean() { return new HelloMessageGenerator(); }

El atributo proxyMode es necesario porque, en el momento de la instanciación del contexto de la aplicación web, no hay una solicitud activa. Spring creará un proxy para inyectarlo como dependencia y creará una instancia del bean de destino cuando sea necesario en una solicitud.

También podemos usar una anotación compuesta de @RequestScope que actúa como un atajo para la definición anterior:

@Bean @RequestScope public HelloMessageGenerator requestScopedBean() { return new HelloMessageGenerator(); }

A continuación, podemos definir un controlador que tiene una referencia inyectada a requestScopedBean . Necesitamos acceder a la misma solicitud dos veces para probar los ámbitos específicos de la web.

Si mostramos el mensaje cada vez que se ejecuta la solicitud, podemos ver que el valor se restablece a nulo , aunque luego se cambia en el método. Esto se debe a que se devuelve una instancia de bean diferente para cada solicitud.

@Controller public class ScopesController { @Resource(name = "requestScopedBean") HelloMessageGenerator requestScopedBean; @RequestMapping("/scopes/request") public String getRequestScopeMessage(final Model model) { model.addAttribute("previousMessage", requestScopedBean.getMessage()); requestScopedBean.setMessage("Good morning!"); model.addAttribute("currentMessage", requestScopedBean.getMessage()); return "scopesExample"; } }

4.2. Alcance de la sesión

Podemos definir el bean con alcance de sesión de manera similar:

@Bean @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator sessionScopedBean() { return new HelloMessageGenerator(); }

También hay una anotación compuesta dedicada que podemos usar para simplificar la definición de bean:

@Bean @SessionScope public HelloMessageGenerator sessionScopedBean() { return new HelloMessageGenerator(); }

Next, we define a controller with a reference to the sessionScopedBean. Again, we need to run two requests in order to show that the value of the message field is the same for the session.

In this case, when the request is made for the first time, the value message is null. But once, it is changed, then that value is retained for subsequent requests as the same instance of the bean is returned for the entire session.

@Controller public class ScopesController { @Resource(name = "sessionScopedBean") HelloMessageGenerator sessionScopedBean; @RequestMapping("/scopes/session") public String getSessionScopeMessage(final Model model) { model.addAttribute("previousMessage", sessionScopedBean.getMessage()); sessionScopedBean.setMessage("Good afternoon!"); model.addAttribute("currentMessage", sessionScopedBean.getMessage()); return "scopesExample"; } }

4.3. Application Scope

The application scope creates the bean instance for the lifecycle of a ServletContext.

This is similar to the singleton scope but there is a very important difference with regards to the scope of the bean.

When beans are application scoped the same instance of the bean is shared across multiple servlet-based applications running in the same ServletContext, while singleton-scoped beans are scoped to a single application context only.

Let's create the bean with application scope:

@Bean @Scope( value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator applicationScopedBean() { return new HelloMessageGenerator(); }

Analogously as for the request and session scopes, we can use a shorter version:

@Bean @ApplicationScope public HelloMessageGenerator applicationScopedBean() { return new HelloMessageGenerator(); }

Now, let's create a controller that references this bean:

@Controller public class ScopesController { @Resource(name = "applicationScopedBean") HelloMessageGenerator applicationScopedBean; @RequestMapping("/scopes/application") public String getApplicationScopeMessage(final Model model) { model.addAttribute("previousMessage", applicationScopedBean.getMessage()); applicationScopedBean.setMessage("Good afternoon!"); model.addAttribute("currentMessage", applicationScopedBean.getMessage()); return "scopesExample"; } }

In this case, value message once set in the applicationScopedBean will be retained for all subsequent requests, sessions and even for a different servlet application that will access this bean, provided it is running in the same ServletContext.

4.4. WebSocket Scope

Finally, let's create the bean with websocket scope:

@Bean @Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator websocketScopedBean() { return new HelloMessageGenerator(); }

WebSocket-scoped beans when first accessed are stored in the WebSocket session attributes. The same instance of the bean is then returned whenever that bean is accessed during the entire WebSocket session.

We can also say that it exhibits singleton behavior but limited to a WebSocket session only.

5. Conclusion

Hemos demostrado diferentes ámbitos de frijol proporcionados por Spring y cuáles son sus usos previstos.

La implementación de este tutorial se puede encontrar en el proyecto GitHub; este es un proyecto basado en Eclipse, por lo que debería ser fácil de importar y ejecutar tal como está.