domingo, 27 de abril de 2014

Spring Boot

spring-boot-logo.png


Recientemente ha sido liberada la primera versión General Availability de Spring Boot, hecho que nos complace en MediaNet, ya que durante los últimos meses hemos estado trabajando con versiones Release Candidate previas en algún que otro proyecto.


En MediaNet somos muy cautos con esto de introducir nuevas tecnologías en los proyectos solamente porque suene “cool” decir que trabajamos con lo último que se está cocinando en el mercado. Queremos centrar nuestros esfuerzos precisamente en el proyecto, no en factores externos como pueda ser una librería que aún no está lo suficientemente estable para ser usada en producción.


Entonces, ¿cómo es que decidimos ser early adopters con Spring Boot?


Muy sencillo: aunque es verdad que Spring Boot es un proyecto nuevo dentro del portfolio de Spring, está basado en unos cimientos que ya están de sobra asentados y convenientemente testados, como es el propio framework de Spring.


Pero ¿qué es y qué aporta de nuevo Spring Boot?


Spring Boot nace como resultado de una petición de mejorar el soporte para aplicaciones web “containerless”.  Tradicionalmente, las aplicaciones web hechas con Spring se han desplegado en un contenedor de servlets (Tomcat, Jetty, etc.)  y, se quiera o no, esto añade un nivel más de complejidad en el proyecto. Por tanto, hay que configurarlo para que se despliegue en dicho contenedor, y esto incluye tareas como:


  • Configuración del fichero web.xml
  • Generación del fichero .war
  • Configuración específica del contenedor
  • Configuración del ContextRoot
  • etc.


Frente a esto, el equipo de SpringSource se planteó cómo poder crear una aplicación Java normal y que al ejecutarse se configure “automágicamente”, como si se hubiese desplegado en un contenedor de servlets pero sin todos sus handicaps, y ahí es donde Spring Boot tiene su principal fortaleza.


Vamos a profundizar un poco y ver cómo Spring Boot hace toda esa “magia”.


Starter POMs



Atrás quedaron los ficheros pom de cientos de líneas sólo para especificar las dependencias. Spring Boot nos ofrece otra alternativa a través de una serie de starters que definen las dependencias más comunes que necesitaríamos por tipo de aplicación (o funcionalidad a incluir). Así pues, si por ejemplo quisiéramos en nuestra aplicación utilizar Spring Security nos bastaría con incluir en nuestro pom(*)  algo como:


<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>


(*) Spring Boot también soporta Gradle además de Maven


Si nos fijamos como está definido spring-boot-starter-security podemos observar que internamente lo que hace es definir las siguientes dependencias:


  • spring-boot-starter
  • org.springframework.security:spring-security-config
  • org.springframework.security:spring-security-web
  • org.springframework:spring-aop
  • org.springframework:spring-beans
  • org.springframework:spring-context
  • org.springframework:spring-core
  • org.springframework:spring-expression
  • org.springframework:spring-web


De entre los starters ofrecidos, quiero destacar uno en particular: spring-boot-starter-actuator. Spring Boot Actuator es un subproyecto de Spring Boot que añade a nuestro proyecto una serie de endpoints típicos en sistemas en producción (health, metrics, info, beans, dump, trace, etc).


Por ejemplo:


- /health mostrará el estado de salud de la aplicación.
- /info mostrará información del proyecto (incluído el branch/commit de Git).
- /dump mostrará un dump de todos los threads.
- /beans mostrará todos los beans del ApplicationContext
- ...


Autoconfiguración



Primero hay que indicarle de algún modo a Spring Boot que intente autoconfigurar la aplicación. Para ello usaremos la anotación @EnableAutoConfiguration en nuestra clase java de configuración.


Una vez arranque la aplicación, Spring Boot intentará configurarla basándose en las dependencias que hayamos añadido y no configurado. Por ejemplo, si encuentra en el classpath HSQLDB y detecta que no hemos configurado un dataSource entonces configurará uno por nosotros que apunte a una base de datos embebida HSQLDB.


¿Cómo hace esto? Spring Boot está basado en Spring 4.0, que incluye funcionalidades como la configuración por clases java o las anotaciones @Conditional que permiten decidir si incluir o no un determinado bean en función de alguna condición. Spring Boot además añade la posibilidad de anotar clases de configuraciones con @ConditionalOnMissingBean. Esta anotación permite decidir si se debe incluir o no alguna determinada configuración  (por ejemplo EmbeddedDataSourceConfiguration.class) en función de si ya está definida o no otra clase (por ejemplo DataSource.class)


Basándose en @ConditionalOnMissingBean, Spring Boot ha definido una serie de clases *AutoConfiguration que se encargarán de definir los diferentes componentes:




Para la ver la lista completa, ver el paquete org.springframework.boot.autoconfigure


Todas estas configuraciones necesitarán de algunos valores por defecto. Por ejemplo: si estamos configurando una base de datos MongoDB a través de MongoAutoConfiguration, de alguna forma tendrá que configurarse el host, el puerto, la base de datos, etc.


¿Qué hace Spring Boot para especificar estos valores? Pues obtenerlos de las propiedades de la aplicación definidas a través de algún PropertySource. ¿Y qué ventaja tiene esto? Pues que nosotros podemos sobreescribir esas propiedades a través de nuestro fichero de propiedades, por ejemplo añadiendo a nuestro fichero application.properties :


  spring.data.mongodb.host=127.0.01
  spring.data.mongodb.port=27017
  spring.data.mongodb.uri=mongodb://localhost/test


Pero la forma en que Spring Boot hace uso de estas properties es un poco diferente a como estamos habituados a verlo. Las clases *AutoConfiguration obtienen las properties a través de clases anotadas con @ConfigurationProperties. Por ejemplo MongoAutoConfiguration hace uso de MongoProperties y es MongoProperites la que está ligada a los valores definidos en los ficheros de properties. Y ¿qué ventaja tiene esto? Pues que Spring Boot se encarga de hacer el binding y la validación de los valores de forma que a nosotros nos permite trabajar con las properties de una forma tipada.


Application properties



Y hablando de propiedades, otra característica a destacar es cómo Spring Boot gestiona la carga de las propiedades de configuración de la aplicación.
Al arrancar, Spring Boot intentará cargar el fichero application.properties del classpath (también soporta ficheros en formato YAML), pero no se queda ahí. Si hemos especificado algún perfil de spring como activo intentará cargar esa variante. También nos permite especificar un fichero de propiedades externo, sobreescribir propiedades a través de argumentos de la línea de comandos, etc :


Todo esto nos permite personalizar al máximo la configuración. Por ejemplo si arrancamos la aplicación con los siguientes argumentos:


-Dspring.profiles.active=dev
-Dspring.config.location=file://c:/config/application-external.yml
--server.port=9000


Obtendríamos un entorno con las propiedades cargadas desde:


  1. classpath:/application.properties  (default)
  2. classpath:/application-dev.properties (variante perfil)
  3. file://c:/config/application-external.yml (fichero externo)
  4. server.port (argumento)


El orden que sigue Spring Boot para gestionar el PropertySource es el siguiente:


  1. Argumentos de línea de comando.
  2. Propiedades del Sistema (System.getProperties())
  3. Variables de entorno del S.O.
  4. Anotaciones @PropertySource en las clases de Configuración
  5. Externas al jar (application.properties incluyendo YAML y variantes de perfil)
  6. Internas al jar (application.properties incluyendo YAML y variantes de perfil)
  7. Propiedades por defecto (SpringApplication.setDefaultProperties())

Packaging



Por defecto al empaquetar una aplicación Spring Boot se generará un fichero .jar que podremos ejecutar como una aplicación java normal que a su vez levantará un Tomcat embebido con nuestra aplicación desplegada.


Pero ¿qué ocurre si no queremos levantarla en un Tomcat embebido, sino que necesitamos un fichero .war para desplegarla en un contenedor de servlet preexistente?. No hay problema: Spring Boot nos provee de unos plugins tanto para Maven como para Gradle que nos permiten generar un fichero .war  para poder desplegarlo en cualquier contenedor de servlets.


Todas estas características (y muchas más) hacen que utilizar Spring Boot en nuestros proyectos sea extremadamente cómodo, rápido, flexible y sobre todo, nos permite mantener toda la potencia que tendríamos con una aplicación desarrollada a la manera “tradicional” pero con menos esfuerzo.


Enlaces de interés