Novedades en Spring 4 (y 2)
Continuamos con el repaso a las novedades de la versión 4 de Spring, en esta ocasión más centrados en las relativas al desarrollo web, servicios REST, etc.
Cómo ya comenté en la primera parte, probablemente el proyecto más popular de Spring sea Spring MVC, y su mayor baza la claridad con la que podemos crear controladores web, bien sea manejando peticiones Ajax o con un estilo más tradicional, redirigiendo a vistas JSP, etc. Es posible incluso configurar fácilmente y para la misma petición diferentes formatos de respuesta (JSON, XML, vista tradicional, CSV, lo que sea) mediante el uso de view resolvers, y se integra muy bien con librerías de plantillas como Apache Tiles.
Otra de las grandes ventajas de Spring MVC es lo rápido que permite desarrollar servicios REST, y por este lado vienen varias mejoras en la versión 4.
Controladores REST
Antes de Spring 4, para crear un servicio REST era necesario el uso de dos anotaciones, @Controller
para anotar la clase que escucha las peticiones, y @ResponseBody
, que anota cada método “handler” en particular, y viene a significar “establece el retorno de este método como body del response”, es decir, se transformará la estructura de datos a un formato JSON o similar como el cuerpo de la respuesta:
En el ejemplo, una petición GET a /greeting32
(@RequestMapping
manejará GET por defecto si no se indica lo contrario), devolverá como respuesta el código “200 OK” y el cuerpo de la respuesta será “Hello World - Spring 32”. De forma similar el cuerpo de la respuesta de /items32
será “[“item1 - Spring 32”,”item2 - Spring 32”]” (formato JSON, transformación realizada mediante la librería Jackson).
Spring 4 introduce pequeñas mejoras para crear este tipo de controladores. Por un lado la anotación @ResponseBody
pasa a ser anotación de método y clase (previamente lo era sólo de método), por lo que si la añadimos a nivel de clase, todos los métodos la heredarán, dejando el código mucho más limpio:
Mejor aún, la gente de Spring pensaría por qué no fusionar estas dos anotaciones en una sola, y así hicieron, dando lugar a @RestController
. El resultado es bastante directo y auto-explicativo:
En el ejemplo veréis además como el servicio /greeting4
devuelve el time zone en su respuesta. He incluido esto en el ejemplo como muestra del soporte a la nueva API para fechas y horas que incluye la versión 8 de Java. Aún no he profundizado en esta API lo suficiente para hablar con propiedad, pero parece ser que al fin se ha llegado a una solución en condiciones para manejar esta información (ya era hora, después de 7 versiones :) ).
Clientes REST
En Spring existía la clase RestTemplate
, que sirve para consumir API’s REST de forma sencilla:
El problema de este cliente es que es síncrono, por lo que las llamadas son bloqueantes, y dificulta en gran medida el desarrollo de aplicaciones multithreading, más que nada porque nos obliga a desarrollar un wrapper asíncrono. Supongo que muchos desarrolladores se quejaron a la comunidad Spring de este asunto, porque la versión 4 viene con su propio cliente asíncrono, AsynRestTemplate
. A grandes rasgos podemos utilizar este cliente de dos formas diferentes:
1- Bloqueando el thread mientras esperamos la respuesta:
La llamada a futureEntity.get()
suspenderá el thread que ejecuta la petición REST hasta recibir la respuesta, por lo que otros threads se podrán seguir ejecutando mientras este está suspendido. Es decir, eliminamos esperas activas y optimizamos recursos.
2- Configurando funciones callback:
Cómo habréis deducido, los métodos callback serán ejecutados en un thread paralelo una vez se obtenga respuesta (exitosa o no) del servicio. Opción más elegante en mi opinión, porque elimina la necesidad de tener que manejar en el thread principal las checked exceptions lanzadas por futureEntity.get()
(InterruptedException
y ExecutionException
), y además separa claramente las acciones a realizar en caso de éxito o fracaso de la petición REST.
Soporte para WebSockets
Siendo honestos, mi conocimiento de WebSockets se limita a lo que he trasteado con Spring 4 para escribir este post, así que es posible que meta algún gambazo en mi explicación. No obstante, el ejemplo que voy a mostrar (como siempre, subido a mi repositorio GitHub) funciona perfectamente.
WebSockets es un protocolo que permite el envío de mensajes en ambas direcciones, cliente => servidor y servidor => cliente, sin necesidad de petición previa por parte del cliente, más allá de la suscripción inicial a un topic (“handshake” inicial, operación realizada mediante HTTP). WebSockets es un protocolo que funciona sobre TCP (más allá del “apretón de manos” inicial), y a su vez necesita un sub-protocolo para gestionar los mensajes enviados. La opción principal en Spring es el protocolo STOMP.
Un problema con los websockets es su soporte por parte de determinados navegadores (adivinad cuáles). Para solventar esta situación, es necesario que exista un protocolo alternativo para el caso en que el navegador en cuestión no soporte WebSockets. Spring utiliza SockJS como “fallback option”.
El ejemplo que mostraré está basado en la documentación de Spring, pero sin utilizar Spring Boot. No explicaré paso a paso el proceso, ya que la documentación es bastante buena, sólo haré hincapié en los principales aspectos a tener en cuenta para configurar una aplicación con WebSockets.
Configuración de WebSockets en la aplicación
Aquí estamos indicando que:
- ésta se trata de una clase de configuración de Spring (
@Configuration
) - se activen el soporte para websockets (
@EnableWebSocketMessageBroker
).
En los métodos indicamos que:
- se cree un broker en memoria que se encargará de enviar mensajes a los clientes suscritos a topics con el prefijo /topic (
config.enableSimpleBroker("/topic")
). Si la palabra broker os suena a chino (como a mí hasta hace unos meses), echad un vistazo al patrón broker - se manejarán mensajes en endpoints prefijados con “/app” (
config.setApplicationDestinationPrefixes("/app")
) - se cree un endpoint “/app/hello” para manejos de mensajes STOMP desde clientes, y con SockJS como opción de respaldo (
registry.addEndpoint("/hello")).withSockJS()
)
Clase controladora
La clase está anotada como cualquier otra clase controladora de Spring MVC (@Controller
). En el método greeting:
- Escuchamos mensajes enviados al endpoint “/app/hello” (
@MessageMapping("/hello")
). La falta del prefijo “/app” en la configuración puede resultar algo confusa - Publicar el mensaje de retorno al topic del broker “/topic/greetings” (
@SendTo("/topic/greetings")
)
Por lo demás, el método espera recibir una instancia correcta de HelloMessage
, y devolverá una instancia de Greeting
Configuración de cliente (Javascript)
(he omitido algunas partes del código que sí está subido a GitHub por claridad)
Aquí vemos que:
- Es necesario incluir las librerías JS
sock-js
ystomp
- En el método
connect
vemos cómo hay que suscribirse a un topic, siendo necesario configurar el nombre completo del topic y la función callback cuando recibamos un mensaje publicado por el broker - En el método
sendName
mandamos un mensaje al endpoint “/app/hello”, que será manejado en el controlador. Nótese como en este ejemplo recibimos el mensaje de vuelta desde el broker en el mismo cliente, pero no tiene por qué ser así - En el método disconnect eliminamos la suscripción al topic
En el siguiente video tenéis una demostración en vivo de la aplicación (siempre es más fácil ver algo en directo para entenderlo del todo :), disculpad la falta de estilos CSS en condiciones). Como se puede ver, se abren dos pestañas a la misma aplicación, y cuando se conecta cada cliente al websocket este empezará a recibir los mensajes publicados sin necesidad de enviar petición previa:
Conclusiones
Terminamos aquí el repaso a las principales novedades de Spring 4. Creo que hay mejoras sustanciales respecto a la versión 3, y no dudo que a este framework aún le queda vida para rato.