Muy a menudo me encuentro, a la hora de llevar a cabo una aplicación, con la necesidad de realizar un gran número de proyectos o módulos para construir servicios que en muchos casos comparten una serie de características en común.
En estas situaciones se suele realizar el servicio totalmente nuevo, o haciendo copy&paste de uno anterior, provocando un aumento en los recursos necesarios para llevarlo acabo y aumentando el riesgo de errores.
Para optimizar esta situación existen los arquetipos de Maven, los cuales son plantillas que se pueden utilizar para crear proyectos, módulos, etc. que se basan en parámetros establecidos en la definición del arquetipo.
¿Qué es un arquetipo?
En esencia, un arquetipo es un patrón o modelo sobre el que se pueden desarrollar todas aquellas tareas que son de un mismo tipo. Puede decirse que son plantillas, parametrizadas o configuradas para utilizar determinadas tecnologías que los desarrolladores utilizan como base para escribir y organizar el código de la aplicación.
Que todos los proyectos que involucren ciertas tecnologías partan de una base (arquetipo, plantilla o esqueleto configurado) común, tiene ventajas evidentes:
- Homogeneidad entre distintos desarrollos que utilizan las mismas tecnologías.
- Reutilización y construcción de unos arquetipos como suma de otros.
- Estandarización de los proyectos dentro de una organización. La homogeneidad en la estructura del proyecto facilita las tareas de desarrollar, desplegar y mantener el proyecto generado.
- Reducción del tiempo necesario para la construcción de los diversos servicios.
Cómo crear un arquetipo
Para crear la base de un arquetipo, disponemos del siguiente comando: mvn archetype:generate -DgroupId=[groupID del arquetipo] -DartifactId=[artifactId del arquetipo] -DarchetypeArtifactId=maven-archetype-archetype
Para nuestro ejemplo ejecutamos el siguiente comando: mvn archetype:generate -DgroupId=com.atSistemas -DartifactId=arquetipoAT -DarchetypeArtifactId=maven-archetype-archetype
Obteniendo la siguiente estructura:
A partir de este arquetipo podríamos generar un proyecto Maven con el siguiente comando: mvn archetype:generate -DarchetypeGroupId=com.atSistemas -DarchetypeArtifactId=arquetipoAT
Que en este caso lo que obtendríamos es el siguiente proyecto:
Como se observa ha construido un proyecto con la misma estructura definida en el arquetipo. Pero esto nos sabe a poco, en la siguiente sección vamos a desarrollar paso a paso un arquetipo en el que se vea la verdadera potencia de los mismos.
Ejemplo de arquetipo
Para ver las posibilidades que ofrecen los arquetipos, vamos a crear un arquetipo que permite crear servicios CQRS con Spring Boot que expongan un API REST. El código del ejemplo se encuentra en GitHub .
Analizando la estructura anterior generada al crear el arquetipo vemos que se compone de:
1.- Un pom.xml a nivel raíz del arquetipo, para poder construirlo.
2.- Los ficheros y carpetas que compondrán el cuerpo del arquetipo. Se sitúan dentro de la carpeta src/main/resources/archetype-resources/ y a partir de ellos se crearán las clases que componen el proyecto final.
3.- Los pom.xml del proyecto o módulos que se crearán a partir del arquetipo.
4.- El descriptor del arquetipo archetype.xml, que se sitúa en la carpeta src/main/resources/META-INF y que indica al mecanismo de generación de arquetipos todo el contenido que se va a generar.
Para comenzar a construir el arquetipo debemos realizar las siguientes tareas:
- Definir la estructura del arquetipo.
- Escribir el contenido de los ficheros que generarán las clases.
- Completar el descriptor del arquetipo.
Estructura del arquetipo
Debemos de planificar la estructura del proyecto final, tanto los paquetes como las clases que lo compondrán.
Para la definición de esta estructura nos apoyaremos en la funcionalidad que proporciona el arquetipo Maven para crear nombres dinámicos tanto de ficheros como de carpetas. Para poder crear un nombre dinámico se debe poner __nombreCorto__
, tanto para carpeta como nombre de fichero, este __nombreCorto__
se expandirá con el contenido definido en el descriptor del arquetipo.
Si quisieramos que en nuestro proyecto final apareciera una interfaz y una clase, con el mismo nombre del artefacto que se va a generar, se debería poner lo siguiente:
Si queremos crear un folder a partir de un atributo llamado resourceName, deberíamos indicar:
En nuestro caso, queremos que se cree un paquete con el nombre del servicio que se va a crear, por lo que crearemos una carpeta llamada __serviceName__
, en la cual se crearán las carpetas que crearán los distintos paquetes del proyecto final.
Para nuestro ejemplo (que como he indicado es un servicio Spring Boot que ofrece un API REST para realizar CRUD sobre un dato), creamos las carpetas y clases necesarias y nuestro arquetipo pasaría a tener el siguiente aspecto:
Contenido de los ficheros
Una vez definida la estructura, el siguiente paso consiste en dar contenido a cada uno de los ficheros, los cuales generaran las clases de nuestro proyecto final, el maven-archetype-plugin utiliza el motor de Velocity, para la generación y conversión de los ficheros que se utilizan como plantilla para la creación de las clases en el proyecto final.
Esto nos permite dentro de la plantilla poner, por ejemplo en el fichero que generará la clase controller:
package ${package}.${serviceNameFolder.replace('/','.')}.rest.controller;
Lo cual en el momento de crear el controlador definirá el paquete correspondiente a la clase.
Si en nuestro caso damos la posibilidad de definir condicionalmente el método DELETE en la interfaz ofrecida podríamos tener dentro del fichero lo siguiente:
#if ($type == "DELETE")
/**
* Borra ${serviceName}.
*
* @param id Integer
* @return ${serviceName}DTO
*/
@ApiOperation(value = "Elimina un ${serviceName}", tags = { "Controlador ${serviceName}s" })
@ApiResponses(value = { //
@ApiResponse(code = 200, message = "${serviceName} eliminado", response = ${serviceName}DTO.class), @ApiResponse(code = 404, message = "No eliminado") })
@RequestMapping(method = RequestMethod.DELETE, value = "{id}")
public ResponseEntity<${serviceName}DTO> delete(@ApiParam(value = "Id del ${serviceName} que se va a crear", required = true) @PathVariable String id) {
${serviceName} ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Deleted = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Service.delete(new Integer(id));
HttpHeaders headers = new HttpHeaders();
${serviceName}DTO output = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Deleted.to${serviceName}DTO();
return new ResponseEntity<>(output, headers, HttpStatus.ACCEPTED);
}
#end
A continuación se muestran algunos ejemplos de los ficheros generados. El ejemplo completo se encuentra en GitHub.
Fichero __serviceName__CommandsController.java
del arquetipo:
package ${package}.${serviceNameFolder.replace('/','.')}.rest.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
import ${package}.${serviceNameFolder.replace('/','.')}.service.domain.${serviceName};
import ${package}.${serviceNameFolder.replace('/','.')}.service.${serviceName}Service;
import ${package}.${serviceNameFolder.replace('/','.')}.rest.dto.${serviceName}DTO;
@RestController
@RequestMapping(value = "/${serviceNameFolder}")
@Api(value = "${serviceName}CommandsController", produces = "application/json")
/**
* Controlador de commands
*
* @author Jose Antonio Navarro janavarro.fuentes@atsistemas.com
*/
public class ${serviceName}CommandsController {
@Autowired
private ${serviceName}Service ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Service;
/**
* Crea ${serviceName}.
*
* @param ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)} ${serviceName}DTO
* @param builder UriComponentsBuilder
* @return ${serviceName}DTO
*/
@ApiOperation(value = "Crea un ${serviceName}", tags = { "Controlador ${serviceName}s" })
@ApiResponses(value = { //
@ApiResponse(code = 200, message = "${serviceName} creado", response = ${serviceName}DTO.class), @ApiResponse(code = 404, message = "No creado") })
@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<${serviceName}DTO> create(@ApiParam(value = "${serviceName} que se va a crear", required = true) @RequestBody @Valid ${serviceName}DTO ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}, UriComponentsBuilder builder) {
${serviceName} ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Created = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Service.save(${serviceName}.from${serviceName}DTO(${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}));
HttpHeaders headers = new HttpHeaders();
headers.setLocation(builder.path("${serviceNameFolder}/{id}")
.buildAndExpand(String.valueOf(${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Created.getId())).toUri());
${serviceName}DTO output = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Created.to${serviceName}DTO();
return new ResponseEntity<>(output, headers, HttpStatus.CREATED);
}
/**
* Actualiza ${serviceName}.
*
* @param ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)} ${serviceName}DTO
* @param builder UriComponentsBuilder
* @return ${serviceName}DTO
*/
@ApiOperation(value = "Actualiza un ${serviceName}", tags = { "Controlador ${serviceName}s" })
@ApiResponses(value = { //
@ApiResponse(code = 200, message = "${serviceName} actualiza", response = ${serviceName}DTO.class), @ApiResponse(code = 404, message = "No actualizado") })
@RequestMapping(method = RequestMethod.PUT)
public ResponseEntity<${serviceName}DTO> update(@ApiParam(value = "${serviceName} que se va a actualizar", required = true) @RequestBody @Valid ${serviceName}DTO ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}, UriComponentsBuilder builder) {
${serviceName} ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Created = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Service.save(${serviceName}.from${serviceName}DTO(${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}));
HttpHeaders headers = new HttpHeaders();
headers.setLocation(builder.path("${serviceNameFolder}/{id}")
.buildAndExpand(String.valueOf(${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Created.getId())).toUri());
${serviceName}DTO output = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Created.to${serviceName}DTO();
return new ResponseEntity<>(output, headers, HttpStatus.ACCEPTED);
}
#if ($type == "DELETE")
/**
* Borra ${serviceName}.
*
* @param id Integer
* @return ${serviceName}DTO
*/
@ApiOperation(value = "Elimina un ${serviceName}", tags = { "Controlador ${serviceName}s" })
@ApiResponses(value = { //
@ApiResponse(code = 200, message = "${serviceName} eliminado", response = ${serviceName}DTO.class), @ApiResponse(code = 404, message = "No eliminado") })
@RequestMapping(method = RequestMethod.DELETE, value = "{id}")
public ResponseEntity<${serviceName}DTO> delete(@ApiParam(value = "Id del ${serviceName} que se va a crear", required = true) @PathVariable String id) {
${serviceName} ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Deleted = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Service.delete(new Integer(id));
HttpHeaders headers = new HttpHeaders();
${serviceName}DTO output = ${serviceName.substring(0,1).toLowerCase()}${serviceName.substring(1)}Deleted.to${serviceName}DTO();
return new ResponseEntity<>(output, headers, HttpStatus.ACCEPTED);
}
#end
}
Resultado en la clase PersonaCommandsController.java
del proyecto final:
package com.atSistemas.persona.gestion.rest.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
import com.atSistemas.persona.gestion.service.domain.Persona;
import com.atSistemas.persona.gestion.service.PersonaService;
import com.atSistemas.persona.gestion.rest.dto.PersonaDTO;
@RestController
@RequestMapping(value = "/persona/gestion")
@Api(value = "PersonaCommandsController", produces = "application/json")
/**
* Controlador de commands
*
* @author Jose Antonio Navarro janavarro.fuentes@atsistemas.com
*/
public class PersonaCommandsController {
@Autowired
private PersonaService personaService;
/**
* Crea Persona.
*
* @param persona PersonaDTO
* @param builder UriComponentsBuilder
* @return PersonaDTO
*/
@ApiOperation(value = "Crea un Persona", tags = { "Controlador Personas" })
@ApiResponses(value = { //
@ApiResponse(code = 200, message = "Persona creado", response = PersonaDTO.class), @ApiResponse(code = 404, message = "No creado") })
@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<PersonaDTO> create(@ApiParam(value = "Persona que se va a crear", required = true) @RequestBody @Valid PersonaDTO persona, UriComponentsBuilder builder) {
Persona personaCreated = personaService.save(Persona.fromPersonaDTO(persona));
HttpHeaders headers = new HttpHeaders();
headers.setLocation(builder.path("persona/gestion/{id}")
.buildAndExpand(String.valueOf(personaCreated.getId())).toUri());
PersonaDTO output = personaCreated.toPersonaDTO();
return new ResponseEntity<>(output, headers, HttpStatus.CREATED);
}
/**
* Actualiza Persona.
*
* @param persona PersonaDTO
* @param builder UriComponentsBuilder
* @return PersonaDTO
*/
@ApiOperation(value = "Actualiza un Persona", tags = { "Controlador Personas" })
@ApiResponses(value = { //
@ApiResponse(code = 200, message = "Persona actualiza", response = PersonaDTO.class), @ApiResponse(code = 404, message = "No actualizado") })
@RequestMapping(method = RequestMethod.PUT)
public ResponseEntity<PersonaDTO> update(@ApiParam(value = "Persona que se va a actualizar", required = true) @RequestBody @Valid PersonaDTO persona, UriComponentsBuilder builder) {
Persona personaCreated = personaService.save(Persona.fromPersonaDTO(persona));
HttpHeaders headers = new HttpHeaders();
headers.setLocation(builder.path("persona/gestion/{id}")
.buildAndExpand(String.valueOf(personaCreated.getId())).toUri());
PersonaDTO output = personaCreated.toPersonaDTO();
return new ResponseEntity<>(output, headers, HttpStatus.ACCEPTED);
}
/**
* Borra Persona.
*
* @param id Integer
* @return PersonaDTO
*/
@ApiOperation(value = "Elimina un Persona", tags = { "Controlador Personas" })
@ApiResponses(value = { //
@ApiResponse(code = 200, message = "Persona eliminado", response = PersonaDTO.class), @ApiResponse(code = 404, message = "No eliminado") })
@RequestMapping(method = RequestMethod.DELETE, value = "{id}")
public ResponseEntity<PersonaDTO> delete(@ApiParam(value = "Id del Persona que se va a crear", required = true) @PathVariable Integer id) {
Persona personaDeleted = personaService.delete(id);
HttpHeaders headers = new HttpHeaders();
PersonaDTO output = personaDeleted.toPersonaDTO();
return new ResponseEntity<>(output, headers, HttpStatus.ACCEPTED);
}
}
Descriptor del arquetipo
El último paso es completar el descriptor del arquetipo. Como he indicado anteriormente es el fichero archetype-metadata.xml
que se encuentra en la carpeta src\main\resources\META-INF\maven
del arquetipo. En el caso del arquetipo de prueba el descriptor es:
<?xml version="1.0" encoding="UTF-8"?>
<archetype-descriptor
xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd"
name="Subprocesses Maven Archetype"
xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<requiredProperties>
<requiredProperty key="groupId">
<defaultValue>com.atSistemas</defaultValue>
</requiredProperty>
<requiredProperty key="version">
<defaultValue>1.0.0-SNAPSHOT</defaultValue>
</requiredProperty>
<requiredProperty key="package">
<defaultValue>com.atSistemas</defaultValue>
</requiredProperty>
<requiredProperty key="type">
<defaultValue>NODELETE</defaultValue>
</requiredProperty>
<requiredProperty key="serviceName" />
<requiredProperty key="serviceNameFolder" />
</requiredProperties>
<fileSets>
<fileSet filtered="true" packaged="true" encoding="UTF-8">
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</fileSet>
<fileSet filtered="true" packaged="true" encoding="UTF-8">
<directory>src/test/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory>src/test/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</fileSet>
</fileSets>
</archetype-descriptor>
Como desplegar un arquetipo
Una vez generado el arquetipo Maven, es necesario instalar la librería JAR correspondiente en el repositorio. Para ello se accede a la URL del repositorio que se este utilizando y se realizan las acciones siguientes:
Almacenar el JAR del arquetipo:
mvn install:install-file -Dfile=arquetipoAT-1.0.0-SNAPSHOT.jar -DgroupId=com.atSistemas -DartifactId=arquetipoAT -Dversion=1.0.0-SNAPSHOT -Dpackaging=jar
Instalación del catálogo:
Para habilitar la visibilidad del arquetipo desde IDE's es necesario llevar a cabo la instalación de un catálogo. Un catálogo es un archivo XML que se encuentra en la ruta del repositorio Maven. Este fichero lo tenemos que editar para añadirle:
<?xml version="1.0" encoding="UTF-8"?>
<archetype-catalog xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0 http://maven.apache.org/xsd/archetype-catalog-1.0.0.xsd"
xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<archetypes>
<archetype>
<groupId>com.atSistemas</groupId>
<artifactId>arquetipoAT</artifactId>
<version>1.0.0-SNAPSHOT</version>
<description>arquetipo AT</description>
</archetype>
</archetypes>
</archetype-catalog>
Utilización del arquetipo de ejemplo
Para ejecutar el arquetipo, y siguiendo el modo estándar de utilización, debemos servirnos del plugin 'archetype' y el goal 'generate', indicando el arquetipo en concreto que queremos ejecutar. Para ello escribimos el siguiente comando: mvn archetype:generate -DarchetypeGroupId=com.atSistemas -DarchetypeArtifactId=arquetipoAT -DarchetypeVersion=1.0.0-SNAPSHOT
En la ejecución se nos indicará que elijamos el artifactId y el serviceNameFolder. Este último sirve para crear la estructura de paquetes del proyecto. Por ejemplo, con el valor 'persona/gestion' se generará una estructura de paquetes como esta: 'com.atSistemas.persona.gestion'.
Otra forma de ejecutar el arquetipo es en modo batch de la siguiente forma: mvn archetype:generate -B -DarchetypeGroupId=com.atSistemas -DarchetypeArtifactId=arquetipoAT -DarchetypeVersion=1.0.0-SNAPSHOT -DartifactId=personaAT -DserviceName=Persona -DserviceNameFolder=persona/gestion -Dtype=DELETE
La siguiente salida indica que se ha generado correctamente el proyecto
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: arquetipoAT:1.0.0-SNAPSHOT
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.atSistemas
[INFO] Parameter: artifactId, Value: personaAT
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: package, Value: com.atSistemas
[INFO] Parameter: packageInPathFormat, Value: com/atSistemas
[INFO] Parameter: package, Value: com.atSistemas
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: serviceNameFolder, Value: persona/gestion
[INFO] Parameter: groupId, Value: com.atSistemas
[INFO] Parameter: serviceName, Value: Persona
[INFO] Parameter: artifactId, Value: personaAT
[INFO] Project created from Archetype in dir: personaAT
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.658 s
[INFO] Finished at: 2018-02-06T11:39:50+01:00
[INFO] Final Memory: 17M/223M
[INFO] ------------------------------------------------------------------------
La estructura del servicio generado es:
Prueba del servicio generado
El último paso que nos queda es probar el servicio generado, para construirlo lanzamos el siguiente comando: mvn clean install
mvn clean install
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building personaAT 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ personaAT ---
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ personaAT ---
.........
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.954 s
[INFO] Finished at: 2018-02-06T11:40:26+01:00
[INFO] Final Memory: 39M/327M
[INFO] ------------------------------------------------------------------------
El proyecto generado para este caso ofrece la siguiente documentación:
Y para ponerlo en funcionamiento lo hacemos con el comando: java -jar personaAT-1.0.0-SNAPSHOT.jar
Lo que nos da un servicio en ejecución, y funcionando
2018-02-06 14:53:51.644 INFO 4164 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8066 (http)
2018-02-06 14:53:51.651 INFO 4164 --- [ main] c.a.persona.gestion.PersonaApplication : Started PersonaApplication in 7.491 seconds (JVM running for 8.296)
Si accedemos al cliente swagger generado, en el puerto indicado en el fichero application.properties
:
Se puede ver el API ofrecido por el servicio y se pueden realizar acciones sobre el mismo:
Creación:
Consulta de todos los elementos:
Modificación:
Consulta de un elemento:
Borrado de un elemento:
Conclusión
A lo largo de este artículo hemos visto la utilidad de la utilización de arquetipos Maven, y con la realización del ejemplo se ha visto que con un coste semejante a la realización de un servicio, se puede llevar a cabo la realización de un arquetipo, a partir del cual se puede proceder a la construcción de sucesivos servicios plenamente funcionales con un coste significativamente inferior.
Otras ventajas adiciones a la reducción de coste, es la homogeneización del código producido y de la documentación generada, lo cuál redunda en una reducción en los fallos y facilita el posterior mantenimiento.
Si te ha gustado el artículo, ¡síguenos en Twitter para estar al día de nuevos posts!