user photo

Desarrollando una API-REST con Spring Boot

Published on
Sergio Perea · 16 min read
java
spring
spring boot

En una API REST las llamadas se implementan como peticiones HTTP cuya interfaz se organiza en recursos. Cada URL representa un recurso. Por eso es importante comprender que las API Rest, al igual que las peticiones HTTP, carecen de estado. Se limitan a recibir y contestar peticiones a través de lenguaje JSON.

En arquitecturas orientadas a microservicios, las API-REST cumplen un papel muy importante, pues el estándar es utilizar una interfaz de APIs totalmente dirigida a RESTful.

En este tipo de arquitecturas es cada vez más importante contar con tecnologías que nos ayuden a agilizar el desarrollo, pero sobre todo, que nos permita crear aplicaciones muy ligeras que consuman el menor número de recursos y es justo allí cuando entra Spring Boot.

Podríamos definir Spring Boot como un acelerador para la creación de proyectos con Spring. La idea es utilizar Spring Boot para desarrollar proyectos con Spring, pero de una manera más ágil, mediante a una serie de convenciones que prevalecen sobre la configuración. Y sobre todo evitándonos tener que perder tiempo en montar configuraciones complejas y pesadas con decenas de ficheros.

El framework Spring

Existen muchos motivos por los que te recomendaría usar Spring sobre otros frameworks Java como Struts. La principal para mí es que no es un framework tan «intrusivo». Utilizando Spring, puedes conseguir una arquitectura modular de tu software. Tus clases no tendrán que heredar de las clases de Spring sino que serán POJOs (Plain Old Java Object). Un POJO es una instancia de una clase que no extiende ni implementa nada en especial a parte de las funcionalidades de tu sistema. Para los programadores Java sirve para enfatizar el uso de clases simples y que no dependen de un framework en especial.Para profundizar en ello te remito a libros como «Clean Code» y «Clean Architecture».

Entonces ¿por qué es un framework? Porque nos proporciona mecanismos para aprovechar tecnologías fiables que ya existen y resuelven muy bien ciertos problemas. Y para utilizar esos mecanismos la clave es la inversión de dependencias.

¿Inversión de dependencias?

La base de Spring, y seguramente su mayor superpoder, es respetar el Principio de Inversión de Dependencias. Este es uno de los principios SOLID más importantes.

¿Y por qué es tan importante? Porque este principio nos garantiza que nuestro sistema será flexible a los cambios. Y esto se debe a que las dependencias que tendremos en nuestro código fuente se basarán en abstracciones. En un lenguaje como Java, esto implica que cuando utilicemos intrucciones como use, import o include, nos referiremos a interfaces o algún tipo de declaración abstracta. Pero tratando de evitar heredar de una clase concreta, porque ello acoplará fuertemente nuestro código y dejará de ser flexible.

Evidentemente, este principio es mejor aplicarlo a clases que no sean «estables», es decir, aquellas que no cambien a menudo. Pero en aquellas que cambian más a menudo, podremos garantizar cierta estabilidad si dichos cambios afectan a su implementación pero no a su interfaz.

El principio de inversión de dependencias también nos puede ayudar, dentro de nuestra arquitectura, a eliminar un ciclo de dependencia. Los ciclos de dependencia suelen provocar fuerte acoplamiento que deriva, a menudo, en que cuando cambias algo en un programa, y parece funcionar, te acabas llevando la sorpresa al día siguiente de que lo has roto por otro sitio.

En este diagrama de dependencia entre componentes, vemos un ciclo. Esta es una situación indeseable, porque si algún componente nuevo debe hacer uso del componente 3, también debe ser compatible con el componente 2. Y esto, llevado a sistemas en los que existen muchos ciclos como este, provoca que cuando cambies cualquier parte de tu código «rompas» otra parte sin quererlo.

Por ello, debemos tratar de eliminar estos ciclos de dependencias. Y para ello, un buen método es la inversión de dependencias. Básicamente, lo que haremos es identificar las clases de un componente que dependen de las del otro componente. Imaginemos que las hemos identificado así:

Lo que vamos a hacer para eliminar este ciclo es crear una interfaz para la clase 2-A que implemente los métodos que necesita la clase 3-A. Esa interfaz la incluímos en nuestro componente 3 y la heredaremos en el componente 2.

Por cierto, en realidad habría otras formas de romper este ciclo. Por ejemplo, podríamos llevarnos las clases 2-A y 3-A a un nuevo componente, de forma que los componentes 2 y 3 dependenderían del nuevo. Pero eso es otra historia. No hemos venido aquí a hablar de arquitectura, este es tema de otro tutorial.

Espero que con estos ejemplos hayas entendido que gracias al Principio de Inversión de Dependencias, nos permitirá desarrollar aplicaciones que no dependan fuertemente de los detalles de implementación del framework, o la Base de Datos. Spring nos permitirá definir todos estos aspectos a través de interfaces, de manera que a nuestro código le de completamente igual nada que no esté en esas interfaces.

Para ello, no invocaremos constructores de ninguna clase.

Inversión de Control

Spring también sigue el principio de inversión de control. Esta nueva filosofía es muy útil cuando se usan frameworks de desarrollo. Es el framework el que toma el control, el que define el flujo de actuación o el ciclo de vida de una petición.

Esto se puede implementar de diversas maneras, pero Spring utiliza el patrón de Inyección de Dependencias de Larman.

El patrón de inyección de dependencia siempre ha sido uno de los conceptos que cuesta entender en el mundo del desarrollo de software sobre todo a la gente que esta empezando.

Normalmente cuando nosotros programamos en el día a día con la programación orientada a objeto nos encontramos construyendo objetos y relacionando objetos utilizando dependencias. Por ejemplo, si tenemos una clase A que tiene una relación de uso de la clase B, tenemos que tener en cuenta que para poder utilizarla debe cumplir dos cosas:

  • Debe conocer su tipo.
  • Debe crear una instancia de la clase B
  • Esto va a crear un fuerte acoplamiento entre ambas clases. De modo que cuando queramos cambiar una, tendremos que cambiar también la otra. Pensemos en un programa real, compuesto por decenas o cientos de clases fuertemente dependientes entre ellas, y podremos visualizar como cualquier cambio en una sola clase puede derrumbar nuestro sorftware como un castillo de naipes.

Por eso, los que queremos hacer las cosas correctamente intentamos dividir un poco más las responsabilidades de los objetos, con diseños algo más complejos pero más fáciles de mantener.

La inyección de dependencias puede realizarse referenciando a las clases de dichas dependencias. Sin embargo, esa no es una buena práctica dado que sus componentes tienen una fuerte relación entre sí, que al final nos supondrá un inconveniente para el mantenimiento del software.

Por eso, en la inyección de independencias, normalmente, se usan interfaces. De esta forma conseguimos abstraer la relación entre una clase A que depende de una clase B sin importar la implementación de cada uno de los dos. De esta forma, conseguimos desacoplamiento.

Spring puede inyectar las dependencias por constructor, método o propiedad, como patrón para garantizar la Inversión de Control.

Arquitectura de Spring

Spring está compuesto por varios módulos. Su arquitectura modular permite que podamos declarar sólo aquellos que realmente vamos a utilizar. Y esto nos proporcionará aplicaciones más compactas y ligeras. En el siguiente capítulo os describirá brevemente las partes más importantes de Spring, necesario antes de meter las manos en harina.

El Core Container

Este es el componente más esencial de Spring. Contiene los siguientes módulos:

  • Core: estamos ante el módulo encargado de apoyar características de las que ya hemos hablado, como la IoC (Inversión de Control) o la ID (Inyección de Dependencias).
  • Bean: Este módulo es el encargado de añadir las típicas Factorías como BeanFactory, encargada de instanciar los distintos beans registrados en el framework. Bean Factory es una versión vitaminada del patrón factoría. Uno de los patrones de diseño más utilizados en Java es el patron Factoría que es un patrón de diseño creacional y que sirve para construir una jerarquía de clases. Es posible que si no conoces este patrón lo hayas utilizado alguna vez sin darte cuenta. Imagina que tienes una clase abstracta de la cual heredan otras clases (por ejemplo A y B). El patrón Factoría consistiría en crear una nueva clase encargada de instanciar los objetos A o B dependiendo de cual queramos instanciar en cada momento.

La clase factoría se implementaría así:

package com.jla;

public class FactoriaPoliza {

public static Poliza getPoliza(String tipo) {
    if (tipo.equals("auto")) {
    return new PolizaAuto();
 }
 else {
    return new PolizaHogar();
 }
 }
}
  • Context: la parte central de este módulo se llama ApplicationContext, y su papel será el de hacer de medio para acceder a cualquier objeto definido en nuestra aplicación.
  • SpEL (Spring Expression Language): consiste en un poderoso lenguaje de expresiones capaz de ayudarnos a consultar y utilizar el grafo de objetos en tiempos de ejecución.

Acceso a datos

  • JDBC: Una capa de abstracción JDBC que permite acceder, directamente, a bases de datos.
  • ORM: proporciona una capa de integración para APIs de mapeo objeto-relacional (ORM) más populares (JPA, Hibernate…).
  • OXM: proporciona una capa de integración para APIs de mapeo objeto-XML.
  • JMS: Java Messaging Service. Tarde o temprano llega el momento en el que todo desarrollador tiene la necesidad de intercomunicar aplicaciones. La mensajería es un método de comunicación entre componentes software o aplicaciones. Los mensajes permiten una comunicación distribuída débilmente acoplada: un componente envía un mensaje a un destino y el receptor lo recoge del mismo.

Transactions: proporciona una sencilla API para la gestión de transacciones programática de una serie de API de transacciones complejas tales como JTA.

###Web

  • Web: este módulo proporciona características que permiten la integración orientada a web.
  • Web-MVC: implementa el patrón Modelo-Vista-Controlador de Spring para aplicaciones web.
  • Web-Socket: para la comunicación cliente servidor via web-sockets.
  • Web-Portlet: proporciona un patrón MVC para ser utilizado en entornos de portlets. Los portlets son componentes modulares de las interfaces de usuario gestionadas y visualizadas en un portal web. Los portlets producen fragmentos de código de marcado que se agregan en una página de un portal.

Otros:

  • AOP: proporciona una implementación de programación orientada a aspectos. a Programación Orientada a Aspectos o POA (en inglés: aspect-oriented programming) es un paradigma de programación que permite una adecuada modularización de las aplicaciones y posibilita una mejor separación de responsabilidades (Obligación o correspondencia de hacer algo).
  • Aspectd: proporciona integración con el módulo Aspects, un framewrok AOP.
  • Implementations: proporciona soporte para instrumentación de clases e implementación de un cargador de las mismas, con el objeto de ser utilizadas en cietas aplicaciones de servidor.
  • Messaging: soporte para STOMP como un sub-protocolo Web-Socket.
  • Test: soporte para automatizar pruebas de componentes Spring.

El contenedor IOC de Spring

Se trata del núcleo de Spring. Es el contendor que se encarga de crear los objetos, enlazarlos, gestionarlos, configurarlos, destruirlos.

Como ya dijimos en la parte 1 de este tutorial, este contenedor utiliza la Inyección de Dependencias para gestionar componentes y configurarlos. Los objetos que se generan se llaman Beans.

Este contendor debe saber que objetos debe instanciar y ensamblar. Ello se lo indicamos con metadatos. A partir de estos metadatos y las clases POJO de nuestra aplicación, podemos iniciar el contexto de nuestra aplicación. Y a partir de ahí, comenzar la ejecución.

En sus primeras versiones, iniciábamos el contenedor IOC de Spring utilizando archivos XML. Pero hoy se usa una manera más sencilla: las anotaciones.

Para iniciar la configuración del IOC de Spring, partiremos de una clase de configuración. para indicar que una clase es de configuración, utilizamos la anotación * @configuration *.

Pero veámoslo con un ejemplo. El ejemplo más sencillo posible: un «Hola Mundo».

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// Nuestra clase POJO
public class HelloWorld {
    private String messaje;
    public void setMessaje(String messaje) {
        this.messaje = messaje;
    }
    public String getMessaje() {
        return messaje;
    }
}


@Configuration
public class MyConfiguration {
    @Bean(name="Hello")
    public HelloWorld HelloWorld() {
        return new HelloWorld();
    }
}

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext app = SpringApplication.run(DemoApplication.class, args);
        HelloWorld helloWorld = app.getBean("Hello");
        helloWorld.setMessaje("Hola Mundo");
    }
}

En el código vemos que hemos creado una clase POJO llamada HelloWorld. Aquí vemos lo dicho anteriormente: esta clase, que contiene la lógica de negocio de nuestra aplicación, no hereda ni implementa nada de Spring.

Después creamos la clase Configuration que es la que nos permite agregar el Bean HolaMundo al contexto de nuestra aplicación. La distinguimos por la anotación @Configuration. Con la anotación @Bean lo que indicamos a ese método es que va a ser la fuente de definición de Beans.

A partir de ahí, sólo tenemos que crear una aplicación Spring, que además contendrá el método estático main para ser el punto de entrada de nuestra aplicación.

El método main será el encargado de ejecutar la aplicación Spring pasándole el contexto que hemos creado. Para ello usaremos el método run, que retorna un objeto del tipo ConfigurableApplicationContext. Dicho objeto contiene el contexto de la aplicación recién creada.

Y cuando ya hemos iniciado la aplicación y cargado el contexto de la misma, podemos ponernos a instanciar e iniciar Beans. En este caso nuestra clase Pojo.

De momento, esto ni siquiera se acerca a una aplicación web real. Pero vamos entendiendo como añadir y configurar nuestros Beans. Pronto entraremos en harina, no te impacientes.

Los Beans

Cualquier objeto que gestione nuestro IOC de Spring es un Bean. Para ello, en la propia definición de cada Bean lo que haremos será indicar la información necesaria para que el contenedor IOC de Spring sepa como tiene que utilizarlo.

El ciclo de vida de un Bean es muy simple: Spring permite especificar dos métodos. Uno de ellos se ejecutará después de instanciar el Bean. Otro antes de destruirlo. La forma de indicar estos métodos es a través de anotaciones:

@Bean(initMethod="metodoInicial",destroyMethod="metodoFinal")

La información que necesitamos a la hora de definir un Bean es:

  • El nombre de la clase que va a representar el Bean.
  • El nombre del Bean.
  • El scope (ámbito) de los objetos creados para este Bean.
  • Qué es lo que ejecutaremos cuando lo creamos.
  • Qué es lo que ejecutaremos cuando lo destruyamos.
  • De qué otros Beans depende.

Y cuando definimos el Bean, una vez disponemos de esta información podremos hacerlo de dos formas:

  1. Como vimos en el post anterior. Es decir, declarando una clase de configuración del Bean.
  2. Añadiendo al Bean una anotación especial. Dicha anotación puede ser: @Controller, @Service, @Repository… y más tipos predefinidos que forman parte de Spring.

En el ejemplo del artículo anterior (HolaMundo), podríamos usar el segundo método simplemente quitando la clase ConfiguracionApp y añadiendo la anotación @Component(name=»nombreDelBean») a nuestra clase POJO.

El ámbito de un Bean

El ámbito es la forma de indicar como se crea el Bean y en qué contextos se utiliza. Para indicar dicho ámbito utilizaremos la anotación @Scope. Spring nos provee de los siguientes ámbitos posibles para un Bean:

  • Prototype: Este ámbito indica que podemos crear más de una instancia del Bean en el contenedor IoC.
  • Singleton: al contrario que Prototype, sólo permitiremos una única instancia del Bean.
  • Request: se utiliza en las aplicaciones web y su nombre define perfectamente para qué sirve.
  • Session: lo mismo que Request.
  • Global-Session: este ámbito permite definir el Bean en una sesión global de una aplicación web.

Veamos, por ejemplo, como definiríamos un Bean Singleton en base al código del ejemplo del capítulo 2:

@Configuration
public class MyConfiguration {
    @Bean(name="Hello")
    @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
    public HelloWorld HelloWorld() {
        return new HelloWorld();
    }
}

Inyección de dependencias

La inyección de dependencias es uno de los rasgos distintivos de Spring. Al igual que cualquier técnica o herramienta tiene sus ventajas e inconvenientes ya que un uso indebido, pensando que nos va a solucionar nuestros problemas, se puede volver en nuestra contra creando una arquitectura compleja e innecesaria. Pero lo cierto es que, bien utilizado, este patrón casa muy bien con la idea de arquitectura limpia, ya que permite que nuestras piezas de software sean independientes comunicándose únicamente a través de un interface.

La complejidad de los sistema de software ha ido aumentado mucho en los últimos años. Por eso es cada vez más necesario el uso de arquitecturas limpias que nos permitan separar las responsabilidades mediante capas y definiendo reglas de dependencias entre ellas. Todo esto evitará el acoplamiento de nuestro dominio con elementos externos.

Básicamente, lo que conseguimos con este patrón de diseño es suministrar objetos a una clase en vez de permitir que esa clase tenga que crear el objeto. Por tanto, lo que hacemos es una inversión de control, eliminando dependencias entre clases, de tres formas posibles:

Inyección de dependencias basada en constructor.

En este caso utilizamos la anotación @Autowired (no necesaria si sólo existe un constructor).

public class MiClase {

    private claseSecundaria variablePrivada;

    @Autowired
    public MiClase(claseSecundaria variableInyectada) {
        this.variablePrivada = variableInyectada
    }

}

Inyección de dependencias basada en setter.

Aquí lo que hacemos es implementar un método setter.

public class MiClase {

    private claseSecundaria variablePrivada;

    @Autowired
    public void setClaseSecundaria(claseSecundaria variableInyectada) {
        this.variablePrivada = variableInyectada
    }

}

Inyección de dependencias basada en campo.

En este caso lo que hacemos es poner, junto a la anotación @Autowired la declaración del atributo o campo. Para poder realizar este tipo de inyección de dependencias, Spring utiliza la reflexión de Java.

public class MiClase {

    @Autowired
    private claseSecundaria variablePrivada;

    public MiClase() {
    }

}

Cuidado con las dependencias circulares

Este tipo de dependencias surge en su forma más simple cuando un Bean A depende de otro B que a su vez depende de A.

Esto provocará errores ya que Spring tratará de determinar el orden correcto en el que los Beans deberán ser creados, pero cuando existen ciclos esto no es posible. Si además utilizamos inyección de dependencias por constructor, obtendremos un error de compilación.

Robert C. Martin, en su libro de Clean Architecture habla de este antipatrón y cómo evitarlo rediseñando nuestras dependencias para cumplir el Principio de Dependencia Acíclica (ADP). No entraré demasiado aquí en el tema porque nos extenderíamos a conceptos de arquitectura y no es el sitio. Simplemente señalar que es un problema evitable rompiendo el ciclo utilizando el principio de inversión de dependencia y agregando una interfaz entre los componentes.

Lo comento para que lo tengas en cuenta, porque es una fuente muy frecuente de errores entre quienes comienzan con Spring.

Por cierto, que si quisiéramos evitar el error de compilación (no recomendable) sin arreglar el antipatrón, podríamos utilizar la anotación @Lazy para obligar a Spring a que realizase una evaluación perezosa de los Beans o cambiar la inyección de dependencias basada en constructor por la que está basada en setter o un campo.

Bueno, con estos artículos ya hemos sentado las bases para meternos en harina con Spring Boot. A partir del siguiente post empezaré con ello.