Eventos con Spring Framework

0
786

Introducción

Una de las herramientas más potentes y que pasamos más por alto en Spring son los eventos. Gracias al uso de eventos podemos hacer que nuestros componentes compartan cualquier tipo de información a la vez que reducimos el acoplamiento entre ellos.

En una aplicación “típica” en la que hay llamadas directas entre componentes, lo más normal es que los componentes de una aplicación se llamen entre sí. Esto genera mucho acoplamiento entre ellos y hace que unos componentes tengan que tener conocimiento explícito de a qué componentes llamar cuando una acción se realiza y, por lo tanto, aumente la complejidad de los componentes y su mantenimiento.

Con una solución con eventos, podemos delegar en terceros la posibilidad de escuchar determinado evento para realizar cierta acción cuando algo ha ocurrido. De esta manera reducimos la complejidad de cada componente, el acoplamiento y reducimos el esfuerzo de mantenimiento

Pues bien, para solucionar todo esto, Spring Framework nos proporciona un sistema de eventos muy sencillo de usar.

Cabe destacar que esta solución es válida para componentes que comparten el mismo contexto de aplicación. Es decir, que si nuestro objetivo es hacer microservicios, necesitaremos de un broker de mensajes externo (Rabbit MQ, Kafka, etc.) que se encargue de publicar dichos eventos para terceros.

Si nuestra solución pasa por crear un monolito bien modularizado, Spring Events nos vale perfectamente para solucionar el problema.

Componentes

Para poder comenzar con Spring Events. Necesitamos conocer los componentes principales que compondrán la solución:

  • Evento: los eventos son clases serializables que contendrán la información que se va a intercambiar cuando un suceso ocurra. Véase como ejemplo: «se ha creado un usuario»
  • Publisher: será el encargado de publicar un evento, de un determinado tipo, en el contexto de la aplicación.
  • Listener: componente que estará escuchando si un evento ha ocurrido en el contexto de la aplicación. En el momento que dicho evento haya sucedido, el listener capturará dicho evento y realizará las operaciones que deba hacer en ese caso.

 

Manos a la obra!

Creando un evento de aplicación

Como hemos comentado anteriormente, un evento es una clase serializable que contendrá toda la información que se va a intercambiar entre los distintos componentes de mi aplicación. 

Para ello basta con crear una clase que extienda de ApplicationEvent. Así nuestro evento heredará el timestamp que contendrá el instante en el que el evento se creó y el source, que es el objeto que creó dicho evento.

Para este ejemplo vamos a crear un evento cuando un usuario ha sido creado:

Desde Spring 4.2 se permite poder crear eventos sin necesidad de extender de ApplicationEvent, por lo que simplificamos aún más nuestra clase:

* Estoy usando Lombok para generar el constructor, getters y setters de la clase

Publicando mi evento

Una vez hemos creado el evento, necesitamos publicarlo en el contexto de la aplicación. Para ello necesitamos de un publisher que se encargue de ello.

Para poder publicar los eventos, nuestro componente usará el ApplicationEventPublisher el cual será el encargado de publicar dichos eventos en el contexto de nuestra aplicación.

Escuchando el evento

Una vez publicado el evento, lo siguiente será crear los componentes que van a estar escuchando a dicho evento. Para ello tenemos 2 opciones:

  • Implemente la interfaz ApplicationListener:

  • Anotar el método de nuestro componente con la anotación @EventListener:

Cualquiera de las dos opciones son perfectamente válidas aunque yo prefiero usar la segunda por sencillez.

Probando nuestra aplicación

Para probar la aplicación lanzaremos un test que lance un evento y veremos en el log la traza que dejará nuestra aplicación:

Como podemos ver, el publisher publica un evento y el listener lo recibe correctamente. Pero hay algo aquí que debemos de tener en cuenta. La publicación del evento es síncrona, por lo tanto, todo el proceso de publicación/recepción del evento se realiza en el mismo hilo.

El principal objetivo de usar eventos es reducir el tiempo de respuesta de nuestro sistema y lo más lógico sería que el sistema funcionase de forma asíncrona. Para ello, crearemos otro Listener que funcione de forma asíncrona y así podremos comprobar que, efectivamente, los eventos podemos procesarlos de forma independiente para que el hilo principal pueda liberarse una vez un evento ha sido publicado.

Para poder escuchar los eventos de forma asíncrona, basta con usar la anotación @Async:

Volvemos a ejecutar nuestro test y ahora sí que podemos comprobar que el listener está escuchando los eventos en un hilo distinto al principal:

Conclusiones

En este tutorial hemos visto cómo, de forma sencilla, podemos desacoplar nuestros componentes y aprovechar la potencia que nos dan los eventos para reducir el acoplamiento y el mantenimiento de nuestro sistema. En tutoriales posteriores veremos cómo podemos usar brokers externos y aplicar patrones de segregación de responsabilidad que harán de nuestra aplicación una solución más robusta, que maximizará el rendimiento, la escalabilidad y la seguridad.

Recursos

Puedes ver el código de la aplicación aquí

Dejar respuesta

Please enter your comment!
Please enter your name here