El uso de Reactor Framework poco a poco va introduciendose en el mundo de Spring Framework . La programación reactiva es algo que más pronto que tarde tendremos que ir asumiendo en nuestro día a día y las nuevas arquitecturas web nos acercaran a modelos como el de Node.js pero desde del mundo Java.
Reactor Framework y configuración
Lo primero que tendremos que hacer es construirnos un sencillo proyecto Maven que incluya las librerías de Reactor.
<dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-core</artifactId> <version>3.3.1.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/io.projectreactor/reactor-test --> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <version>3.3.1.RELEASE</version> <scope>test</scope> </dependency>
Usando la clase Mono
La clase más sencilla de la que dispone Reactor es la clase Mono que hace referencia a una variable asíncrona sencilla . Es decir estamos ante un elemento que se ejecuta y que lo hace de forma asíncrona.
Se puede almacenar una variable de tipo básico o un objeto. Veamos algunos de los ejemplos.
@Test public void testTexto() { Mono<String> mensaje=Mono.just("hola"); mensaje.subscribe(System.out::println); }
En este primer bloque de código lo único que hacemos es generar un objeto mono y subscribirnos a su flujo . En este caso es algo tan sencillo que simplemente nos mostrará el texto “hola” por la consola.
Hasta aquí todo muy sencillo . El siguiente paso es construirnos una clase y generar un objeto Mono con ella.
package com.arquitecturajava; public class Persona { private String nombre; private String apellidos; private int edad; public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } public String getApellidos() { return apellidos; } public void setApellidos(String apellidos) { this.apellidos = apellidos; } public int getEdad() { return edad; } public void setEdad(int edad) { this.edad = edad; } public Persona(String nombre, String apellidos, int edad) { super(); this.nombre = nombre; this.apellidos = apellidos; this.edad = edad; } @Override public String toString() { return "Persona [nombre=" + nombre + ", apellidos=" + apellidos + ", edad=" + edad + "]"; } }
Creamos un bloque de código que nos puede ejecutar una variable de tipo Mono con este objeto.
@Test public void testObjeto() { Mono<Persona> mensaje=Mono.just(new Persona("pepe","perez",20)); mensaje.subscribe(System.out::println); }
El resultado por la consola es similar al resultado de la primera ejecución solamente que en este caso obtenemos los datos del propio objeto.
La clave aquí se encuentra en que el objeto Mono es un objeto asíncrono y se resolverá en un tiempo futuro.
En este caso su resolución es inmediata ya que no tenemos ningún tiempo de espera . Sin embargo esto lo podemos variar ya que el objeto lo permite en este caso podríamos escribir :
@Test public void testAsincrono() throws InterruptedException { Mono<Persona> mensaje=Mono.just(new Persona("pepe","perez",20)).delayElement(Duration.ofSeconds(5)); mensaje.subscribe(System.out::println); Thread.sleep(10000); }
Es aquí donde le pedimos al objeto mono que espere un tiempo antes de resolverse usando el método delayElement. Obtendremos el mismo resultado pero tendremos que esperar esos 5 segundos para verlo en la consola
Es aquí donde Reactor Framework nos ayuda a simplificar las cosas y hace que la tarea sea trivial.
Usando la clase Flux
Flux a diferencia de Mono se encarga de gestionar una lista de elementos asíncronos.
Nos aporta mayor flexibilidad en su uso . Veamos un ejemplo:
@Test public void testLista() throws InterruptedException { Flux<String> mensaje=Flux.just("hola" ,"que" ,"tal","estas","tu").delayElements(Duration.ofSeconds(1)); mensaje.subscribe(System.out::println); Thread.sleep(10000); }
En este caso estamos recorriendo con el método subscribe una lista de elementos cada uno de los cuales nos lleva segundo a segundo. La ventaja de Reactor Framework es que nos permite combinar estructuras asíncronas de forma muy sencilla . Por ejemplo podemos usar el método concat para combinar dos flujos asíncronos.
Flux<String> mensajes1=Flux.just("hola" ,"que" ,"tal","estas","tu").delayElements(Duration.ofSeconds(1)); Flux<String> mensajes2=Flux.just("hola2" ,"que2" ,"tal2","estas2","tu2").delayElements(Duration.ofSeconds(1)); mensajes1.concatWith(mensajes2).subscribe(System.out::println); Thread.sleep(15000);
Esto hará que la lista de elementos nos salga en pantalla una a una . Primero la primera lista de elementos segundo a segundo. Para luego imprimir de la misma forma la segunda.
Este es un ejemplo de cómo podemos usar Flux y Reactor para combinar dos listas de elementos asíncronos. Sin embargo Reactor soporta situaciones mucho más diversas. Por ejemplo podemos querer esperar hasta tener un numero concreto de elementos antes de imprimirlos . Esta es una operación asíncrona básica y se denomina buffer ,vamos a verla.
@Test public void testLista3() throws InterruptedException { Flux<String> mensajes1=Flux.just("hola" ,"que" ,"tal","estas","tu").delayElements(Duration.ofSeconds(1)); Flux<String> mensajes2=Flux.just("hola" ,"que" ,"tal","estas","tu").delayElements(Duration.ofSeconds(1)); mensajes1.concatWith(mensajes2).buffer(3).subscribe(System.out::println); Thread.sleep(15000);
Como se puede observar el código prácticamente no cambia simplemente añadimos la operación de buffer. Ahora bien el resultado en la consola cambia de forma significativa.
En este caso se van almacenando los elementos para devolverlos como un array hasta llegar a 3. El framework Reactor es el encargado de simplificar los flujos de programación asíncrona complejos y es la base de Spring WebFlux como framework asíncrono de servidor a nivel de aplicaciones web
Tengo una pequeña duda cual es la función o para que usar
Perfecto, maravilloso, pero tengo una duda:
Todo esto de reactividad es maravilloso por el lado del backend. PERO!, nosotros queremos que reaccione el FRONT END cuando la respuesta este lista.
¿Podrías dar un ejemplo sencillo de como lograr esto?.
¿Cual es la ventaja de esto con usar promesas en Javascript?. ¿No logro el mismo efecto?.
Gracias.
Podrías usar un server send event 🙂
Gracias como siempre por tan buenos artículos!!!