Академический Документы
Профессиональный Документы
Культура Документы
com
21/02/201822/03/2019
Última actualización: 22/03/2019
Teniendo en cuanta esta problemática, allá por 2014 se publicó la primera versión
estable de Spring Boot (https://spring.io/projects/spring-boot), un módulo que
pretende solucionar estos problemas y acelerar el desarrollo de aplicaciones basadas en
Spring gracias a una gestión automatizada de la infraestructura software que
necesitamos en nuestra aplicación. En concreto: “Spring Boot proporciona una manera
rápida de construir aplicaciones. Inspecciona el classpath y los beans que tengas
con�gurado, hace asunciones razonables sobre lo que te falta y lo añade. Con Spring
Boot puedes centrarte más en la lógica de negocio y menos en la infraestructura”.
Hay que tener presente que Spring Boot no es un framework al uso ni un generador de
código, se centra en la con�guración e integración de las dependencias que solemos
necesitar de forma genérica para que sin ningún esfuerzo empecemos directamente a
desarrollar nuestra aplicación. Incluso con�gura de forma embebida un servidor
Tomcat o Jetty si así lo deseamos. Y lo mejor de todo es que no supone una limitación
ya que podemos seguir realizando cualquier con�guración de igual forma que si no
tuviéramos Spring Boot.
En este tutorial daremos los primeros pasos para empezar a utilizar Spring Boot en
nuestros proyectos. En concreto, crearemos una aplicación web con una pantalla que
muestre un listado de datos obtenidos desde una tabla de una base de datos MySQL, así
como una pequeña API REST de ejemplo. Asumo que el lector tiene conocimientos, a
nivel muy básico, de Maven, Spring Data JPA y Spring MVC porque voy a centrarme
sobre todo en Spring Boot.
Testing en Spring Boot con JUnit 4\5. Mockito, MockMvc, REST Assured, bases de datos
embebidas (https://danielme.com/2018/11/26/testing-en-spring-boot-con-junit-45-
mockito-mockmvc-rest-assured-bases-de-datos-embebidas/)
Se usará Spring Boot 2.1 aunque se indican las diferencias más relevantes con respecto
a Spring Boot 1.5 de las funcionalidades que veamos.
Vamos a crear y con�gurar un proyecto Maven con Spring Boot similar al de los
tutoriales de Spring Data JPA (https://danielme.com/2014/02/08/persistencia-en-bd-
con-spring-data-jpa/) pero con una capa web (JSP y REST). Como es habitual,
empezamos con�gurando el pom.xml con las dependencias que necesitamos, y aquí
viene la primera ventaja de adoptar Spring Boot: no de�niremos como dependencias
directamente los módulos de Spring necesarios (web, orm, security) sino una suerte de
“metadependencias” (starters) de Spring Boot que incluyen todo lo necesario. Las
versiones de las dependencias son debidamente seleccionadas y testeadas por el equipo
de Spring Boot por lo que no tenemos que preocuparnos por posibles
incompatibilidades entre librerías. Para ello añadimos lo siguiente al pom.xml:
1. Utilizar Spring Boot como módulo padre de nuestro proyecto. Usaremos Spring Boot
1 para Spring 4 (el core de Spring es compatible con Java 6 pero dependencias
como Hibernate van a requerir Java 8) y Spring Boot 2 para Spring 5 (Java 8).
1 <p
parent>
2 <ggroupId>org.springframework.boot</g
groupId>
3 <aartifactId>spring-boot-starter-parent</a
artifactId>
4 <vversion>2.1.3.RELEASE</v
version>
5 <rrelativePath />
6 </pparent>
2. Añadir todos los metamódulos (starters) de Spring Boot que encapsulen las
funcionalidades que necesitemos. Los starters disponibles se pueden consultar
directamente en GitHub en este enlace (https://github.com/spring-projects/spring-
boot/tree/master/spring-boot-project/spring-boot-starters). En nuestro caso, para
utilizar Spring Data JPA con Hibernate necesitaremos el siguiente:
1 <d
dependency>
2 <ggroupId>org.springframework.boot</g
groupId>
3 <aartifactId>spring-boot-starter-data-jpa</a
artifactId>
4 </ddependency>
1 <b
build>
2 <pplugins>
3 <pplugin>
4 <g groupId>org.springframework.boot</g
groupId>
5 <a artifactId>spring-boot-maven-plugin</a
artifactId
6 </pplugin>
7 </pplugins>
8 </bbuild>
(https://click.linksynergy.com/fs-bin/click?id=OZ02PWh8Q04&
o�erid=507388.1395&subid=0&type=4)
De�nimos una instancia del logger para una clase del siguiente modo.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
1 <d
dependency>
2 <ggroupId>org.springframework.boot</g
groupId>
3 <aartifactId>spring-boot-starter-log4j2</a
artifactId>
4 </ddependency>
Puesto que seguimos utilizando slf4j como fachada para el sistema de logs no es
necesario cambiar el código de la aplicación.
Llegados a este punto el pom del proyecto inicial (será modi�cado a medida que
avancemos en el tutorial) queda así
1 <?x
xml version="1.0" encoding="UTF-8"?>
Con el comando mvn dependency:list podemos comprobar todas las librerías que
tenemos en nuestro proyecto.
Ahora tenemos que de�nir los parámetros de con�guración de nuestra aplicación (las
propiedades admitidas por las distintas librerías, al �nal de este tutorial explico cómo
añadir propiedades personalizadas). Spring Boot los lee por defecto del �chero
/src/main/resources/application.properties o /src/main/resources/application.yaml y en
este listado (https://docs.spring.io/spring-boot/docs/current/reference/html/common-
application-properties.html) encontramos los más habituales aunque no se incluyen los
de todas las librerías con las que Spring Boot se integra de forma automática.
Para nuestro ejemplo necesitamos como mínimo la url de conexión a la base de datos y
las credenciales. Adicionalmente establecemos también el modo DDL de Hibernate, que
por defecto es none, a update para que se sincronicen nuestras entidades JPA con las
tablas de la base de datos (¡No usar esto en producción si no tenemos claro lo que
estamos haciendo!).
spring.datasource.url=jdbc:mysql://localhost:3306/country
spring.datasource.username=demo
spring.datasource.password=demo
spring.jpa.hibernate.ddl-auto=update
1 package com.danielme.springboot.repositories;
2
3 import java.util.Optional;
4
5 import org.springframework.data.domain.AuditorAware;
6 import org.springframework.stereotype.Component;
7
8 @Component
9 public class CustomAuditorAware implements AuditorAware<String>
10
11 @Override
12 p
public Optional<String> getCurrentAuditor() {
13 r
return Optional.of("test");
14 }
15
16 }
1 package com.danielme.springboot.entities;
2
3 import javax.persistence.Column;
4 import javax.persistence.Entity;
5 import javax.persistence.GeneratedValue;
6 import javax.persistence.GenerationType;
7 import javax.persistence.Id;
8 import javax.persistence.Table;
9 import javax.validation.constraints.Max;
10 import javax.validation.constraints.Min;
11 import javax.validation.constraints.NotNull;
12
13 @Entity
14 @Table(name = "countries")
15 public class Country extends AuditableEntity {
16 @Id
17 @GeneratedValue(strategy = GenerationType.IDENTITY)
18 p
private Long id;
19
20 @Column(nullable = false, unique = true)
21 @NotNull
22 p
private String name;
23
24 @Column(nullable = false)
25 @NotNull
26 @Min(1)
27 @Max(2000000000)
28 p
private Integer population;
29
30 p
public Country() {
31 s
super();
32 }
33
34 p
public Country(String name, Integer population) {
35 s
super();
36 t
this.name = name;
37 t
this.population = population;
38 }
39
40 //getters y setters
41
42 }
1 package com.danielme.springboot.repositories;
2
3 import org.springframework.data.jpa.repository.JpaRepository;
4
5 import com.danielme.springboot.entities.Country;
6
7 public interface CountryRepository extends JpaRepository<Country
8
9 }
1 package com.danielme.springboot.services;
2
3 import java.util.List;
4 import java.util.Optional;
5
6 import org.springframework.stereotype.Service;
7
8 import com.danielme.springboot.entities.Country;
9 import com.danielme.springboot.repositories.CountryRepository;
10
11 @Service
12 public class CountryService {
13
14 p
private final CountryRepository countryRepository;
15
16 p
public CountryService(CountryRepository countryRepository)
17 t
this.countryRepository = countryRepository;
18 }
19
20 ppublic List<Country> findAll() {
21 rreturn countryRepository.findAll();
22 }
23
24 ppublic Optional<Country> findById(Long id) {
25 rreturn countryRepository.findById(id);
26 }
27
28 ppublic Long insert(Country country) {
29 country.setId(nnull);
30 rreturn countryRepository.save(country).getId();
31 }
32 }
1 package com.danielme.springboot;
2
3 import org.slf4j.Logger;
4 import org.slf4j.LoggerFactory;
5 import org.springframework.beans.factory.annotation.Autowired;
6 import org.springframework.boot.CommandLineRunner;
7 import org.springframework.boot.SpringApplication;
8 import org.springframework.boot.autoconfigure.SpringBootApplica
9 import org.springframework.data.jpa.repository.config.EnableJpa
10
11 import com.danielme.springboot.services.CountryService;
12
13 @SpringBootApplication
14 @EnableJpaAuditing(auditorAwareRef = "customAuditorAware")
15 public class DemoApp implements CommandLineRunner {
16
17 p
private static final Logger logger = LoggerFactory.getLogge
18
19 @Autowired
20 CountryService countryService;
21
22 p
public static void main(String[] args) {
23 SpringApplication.run(DemoApp.c
class, args);
24 }
25
26 @Override
27 p
public void run(String... arg0) throws Exception {
28 logger.info(String.valueOf(countryService.findAll().siz
29 }
30
31 }
También podemos utilizar el plugin de Maven de Spring Boot para empaquetar toda la
aplicación en un jar ejecutable sin tener que realizar ninguna acción adicional
utilizando el siguiente comando.
mvn spring-boot:run
Pool de conexiones
HikariCP es bastante más potente que el pool de Tomcat y en los benchmark suele
ofrecer un mejor rendimiento, motivo por el cual el equipo de Spring lo ha adoptado
(https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-
Notes#hikaricp). Sin embargo, HikariCP estaba soportado en Spring Boot 1 y podemos
utilizarlo siguiendo estos dos pasos:
1 <d
dependency>
2 <ggroupId>com.zaxxer</g
groupId>
3 <aartifactId>HikariCP</aartifactId>
4 </ddependency>
En cualquier caso, los parámetros de HikariCP que queramos con�gurar, siempre con
prudencia y en función de las necesidades de la aplicación, se de�nen en este mismo
�chero con el pre�jo spring.datasource.hikari, por ejemplo:
spring.datasource.hikari.maximumPoolSize=20: 10
Convirtamos nuestro ejemplo en una aplicación web con Spring MVC que muestre el
listado de países que tengamos en la base de datos siguiendo los siguientes pasos.
1 <d
dependency>
2 <ggroupId>org.springframework.boot</ggroupId>
3 <aartifactId>spring-boot-starter-web</aartifactId>
4 <eexclusions>
5 <eexclusion>
6 <g groupId>org.springframework.boot</ggroupId>
7 <a artifactId>spring-boot-starter-logging</aartifact
8 </e exclusion>
9 </eexclusions>
10 </ddependency>
1 <d
dependency>
2 <ggroupId>javax.servlet</g
groupId>
3 <aartifactId>jstl</a
artifactId>
4 </ddependency>
1 <d
dependency>
2 <ggroupId>org.apache.tomcat.embed</ggroupId>
3 <aartifactId>tomcat-embed-jasper</a
artifactId>
4 <sscope>provided</s
scope>
5 </ddependency>
3. Spring Boot utiliza un servidor Tomcat, Jetty o Undertow embebido para desplegar
la aplicación. Por defecto se utiliza Tomcat ya que spring-boot-starter-web incluye
spring-boot-starter-tomcat por lo que no tenemos que hacer nada.
4. Las herramientas de desarrollo (https://docs.spring.io/spring-boot/docs/current
/reference/html/using-boot-devtools.html) de Spring Boot permiten, entre otras
funcionalidades, la recarga automática de la aplicación web en el Tomcat al
detectarse cambios en los �cheros incluidos en el classpath.
1 <d
dependency>
2 <ggroupId>org.springframework.boot</ggroupId>
3 <aartifactId>spring-boot-devtools</a
artifactId>
4 <ooptional>true</o
optional>
5 </ddependency>
<p
packaging>war</p
packaging>
Con estos pasos ya hemos convertido nuestra aplicación de ejemplo en una aplicación
web. Vamos a desarrollar un controlador que atienda la raíz de la dirección url para
obtener los países de la base de datos utilizando el repositorio de Spring Data que ya
tenemos pero siempre a través del servicio. Un script JSP será el responsable de
renderizar el html �nal que se enviará al navegador del usuario.
1 package com.danielme.springboot.controllers;
2
3 import org.springframework.stereotype.Controller;
4 import org.springframework.ui.Model;
5 import org.springframework.web.bind.annotation.RequestMapping;
6
7 import com.danielme.springboot.services.CountryService;
8
9 @Controller
10 public class CountryController {
11
12 p
private final CountryService countryService;
13
14 p
public CountryController(CountryService countryService) {
15 t
this.countryService = countryService;
16 }
17
18 @RequestMapping("/")
19 p
public String list(Model model) {
20 model.addAttribute("countriesList", countryService.find
21 r
return "countriesList";
22 }
23 }
Los �cheros con recursos estáticos tales como imágenes, css, JavaScript, etc, se ubican
por defecto en el directorio /src/main/resources/static o en subdirectorios del mismo.
Son accesibles a partir de la url raíz de la aplicación, por ejemplo /src/main/resources
/static/js/script.js estará en http://localhost:8080/js/script.js (http://localhost:8080
/js/script.js).
Crear un .war ejecutable con el comando que vimos antes (mvn package spring-
boot:repackage).
Lanzar directamente la aplicación con mvn spring-boot:run.
Lanzar la aplicación, tanto en modo “normal” como en debug, en Eclipse utilizando
el plugin de Spring instalable desde el Marketplace. También podemos descargar
desde la web de Spring (https://spring.io/tools/sts/all) un Eclipse ya empaquetado
con este plugin. En cualquier caso, en el menu contextual tanto del proyecto como
de la clase con el Main tendremos la opción Spring Boot App dentro de Run As y
Debug As.
Una vez lanzado el servidor, se puede detener desde la propia consola de Eclipse.
server.servlet.context-path=/spring-boot-demo
server.port=9090
Normalmente necesitaremos empaquetar las aplicaciones web en un war estándar que
pueda ser desplegado directamente en un contenedor o servidor de aplicaciones como
Tomcat. Conseguir esto es tan sencillo como hacer que la clase Main herede de
SpringBootServletInitializer, clase que depende de la versión de Spring Boot:
1 : org.springframework.boot.web.support.SpringBootServletInitializer
(https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/api/org/springframework
/boot/web/support/SpringBootServletInitializer.html)
2 : org.springframework.boot.web.servlet.support.SpringBootServletInitializer
(https://docs.spring.io/spring-boot/docs/current/api/org/springframework
/boot/web/servlet/support/SpringBootServletInitializer.html)
1 @SpringBootApplication
2 public class DemoApp extends SpringBootServletInitializer
3 {
Con este pequeño cambio el war generado con Spring Boot sigue siendo autoejecutable
con el Tomcat embebido pero también puede desplegarse en cualquier Tomcat o
similar (renombrar y utilizar el �chero spring-boot-1.0.war.original). Si queremos
prescindir de la funcionalidad de autoejecución, hay que eliminar del pom las
dependencias spring-boot-starter-tomcat y tomcat-embed-jasper. Esto provoca como efecto
colateral que desde Eclipse la aplicación deba ser lanzada como cualquier proyecto web
Maven estándar en un servidor y no con las herramientas del plugin de Spring.
Servicios REST
Vamos a añadir un pequeño servicio REST para Country. Este servicio se implementa
sin utilizar ninguna funcionalidad especí�ca de Spring Boot y voy a utilizar algunas de
las anotaciones introducidas en Spring 4 tales como @RestController
(https://docs.spring.io/spring-framework/docs/current/javadoc-api/org
/springframework/web/bind/annotation/RestController.html) o @GetMapping
(https://docs.spring.io/spring-framework/docs/current/javadoc-api/org
/springframework/web/bind/annotation/GetMapping.html) (Spring 4.3) que
simpli�can la de�nición de los servicios.
En primer lugar, creamos un controlador de tipo RestController con una url raíz para
todas las llamadas que contendrá
1 @RestController
2 @RequestMapping(CountryRestController.COUNTRY_RESOURCE)
3 public class CountryRestController {
4
5 ppublic static final String COUNTRY_RESOURCE = "/api/country"
Tanto las respuestas y peticiones de nuestro servicio REST se basarán en JSON. Esta
funcionalidad ya la tenemos de serie en Spring y no es necesario con�gurar
absolutamente nada. La conversión entre objetos Java y JSON y viceversa (serialización
y deserialización) es realizada de forma automática por Spring si en el classpath se
encuentra la librería Jackson (https://github.com/FasterXML/jackson) la cual es una
dependencia de spring-boot-starter-web. La instancia del mapeador de Jackson
(ObjectMapper (https://fasterxml.github.io/jackson-databind/javadoc/2.2.0
/com/fasterxml/jackson/databind/ObjectMapper.html)) que Spring Boot crea de forma
automática puede personalizarse via application.properties siguiendo la documentación
o�cial (https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-
mvc.html#howto-customize-the-jackson-objectmapper).
Obtención de un país a partir de su id. Es una llamada HTTP de tipo GET con el
identi�cador del país incluido en la propia url. Para recibir en el método este valor
utilizamos la anotación @PathVariable (https://docs.spring.io/spring-framework
/docs/current/javadoc-api/org/springframework/web/bind/annotation
/PathVariable.html). Si el país existe se devuelve junto con un status code 200, si
no simplemente se retorna un 404.
1 @GetMapping(value = "/{id}/")
2 public ResponseEntity<Country> getById(@PathVariable("id"
3 Optional<Country> country = countryService.findById(id);
4 i
if (country.isPresent()) {
5 r
return new ResponseEntity<>(country.get(), HttpStatus.
6 }
7 r
return new ResponseEntity<>(n
null, HttpStatus.NOT_FOUND);
8 }
Creación de un nuevo país. Es una llamada tipo POST directamente a la url raíz del
servicio y el cliente debe enviar un JSON con el nombre y población del país
{
"name": "Germany",
"population": 79778000
}
Spring initializr
La creación inicial del proyecto se puede realizar de forma grá�ca mediante esta
herramienta vía web (https://start.spring.io/), o bien mediante un asistente en
Eclipse gracias al plugin Spring Tool Suite (https://spring.io/tools/sts) del que ya
hemos hablado. Para utilizarlo nos vamos a “File>New->Other” y seleccionamos
el asistente Spring Boot->Spring Starter Project.
Disponemos de dos scripts, uno para Windows (cmd) y otro para Linux (bash), que
invocaremos con los mismos parámetros que usaríamos si llamáramos directamente
al ejecutable de Maven (mvn).
Propiedades personalizadas
Hemos visto que los parámetros de con�guración de Spring Boot y de las librerías
que integra se de�nen en el �chero application.properties. Para utilizar otros �cheros
de propiedades podemos seguir la praxis habitual de cualquier aplicación Spring.
@SpringBootApplication
@EnableJpaAuditing(auditorAwareRef = "customAuditorAware")
@PropertySource("classpath:song.properties")
public class DemoApp extends SpringBootServletInitializer
{
@Value("${song}")
private String song;
@Value("${song}")
@Autowired
private Environment environment;
...
***************************
APPLICATION FAILED TO START
***************************
Description:
Property: custom.value
Value:
Origin: class path resource [application.properties]:10:15
Reason: must not be blank
Action:
1 @SpringBootApplication
2 @EnableConfigurationProperties(CustomProperties.c
class)
3 public class DemoApp extends SpringBootServletInitializer
4 {
3. Le damos valor a la propiedad (custom.value).
spring.datasource.url=jdbc:mysql://localhost:3306/country
spring.datasource.username=demo
spring.datasource.password=demo
spring.jpa.hibernate.ddl-auto=update
spring.datasource.type= com.zaxxer.hikari.HikariDataSource
spring.mvc.view.prefix= /WEB-INF/jsp/
spring.mvc.view.suffix= .jsp
1 package com.danielme.springboot.controllers;
2
3 import org.slf4j.Logger;
4 import org.slf4j.LoggerFactory;
5 import org.springframework.stereotype.Controller;
6 import org.springframework.ui.Model;
7 import org.springframework.web.bind.annotation.RequestMapp
8
9 import com.danielme.springboot.CustomProperties;
10 import com.danielme.springboot.services.CountryService;
11
12 @Controller
13 public class CountryController {
14
15 p
private static final Logger logger = LoggerFactory.get
16
17 p
private final CountryService countryService;
18 p
private final CustomProperties customProperties;
19
20 p
public CountryController(CountryService countryService
21 CustomProperties customProperties) {
22 t
this.countryService = countryService;
23 t
this.customProperties = customProperties;
24 }
25
26 @RequestMapping("/")
27 p
public String list(Model model) {
28 logger.info(customProperties.getValue());
29 model.addAttribute("countriesList", countryService
30 r
return "countriesList";
31 }
32 }
1 <d
dependency>
2 <ggroupId>org.springframework.boot</g
groupId>
3 <aartifactId>spring-boot-configuration-processor</a
artifa
4 <ooptional>true</o
optional>
5 </ddependency>
Código de ejemplo
El proyecto completo para el presente tutorial y también Testing en Spring Boot con
JUnit 4\5. Mockito, MockMvc, REST Assured, bases de datos embebidas
(https://danielme.com/2018/11/26/testing-en-spring-boot-con-junit-45-mockito-
mockmvc-rest-assured-bases-de-datos-embebidas/) se encuentra disponible en
GitHub (https://github.com/danielme-com/spring-boot-web-spring-data-jpa). Para
más información sobre cómo utilizar GitHub, consultar este artículo
(https://danielme.com/2013/08/07/importar-repositorios-de-github-con-git-
o-eclipse).
Testing en Spring Boot con JUnit 4\5. Mockito, MockMvc, REST Assured, bases de
datos embebidas (https://danielme.com/2018/11/26/testing-en-spring-boot-con-
junit-45-mockito-mockmvc-rest-assured-bases-de-datos-embebidas/)
(https://click.linksynergy.com/fs-
bin/click?id=OZ02PWh8Q04&o�erid=467035.271&subid=0&type=4)
1. Miguel
DICE:
15/03/2019 DE 10:15
Hola Daniel, muy bueno el articulo.
Responder
1. danielme.com
DICE:
20/03/2019 DE 19:52
Acabo de publicar Spring REST: Securización BASIC y JDBC con Spring Security.
Responder
This site uses Akismet to reduce spam. Learn how your comment data is processed.
BLOG DE WORDPRESS.COM.