Dedicamos una entrada anterior a exponer las características generales de Quarkus y cómo proporciona una solución para adaptar Java al mundo de la computación en la nube. En este post vamos a crear nuestra primera aplicación Quarkus. Te enseñamos cómo hacerlo paso a paso.
Qué vas as ver en esta entrada
Desarrollo de una aplicación Quarkus
A continuación, vamos a desarrollar una aplicación Quarkus a través de unos sencillos pasos. Lo primero que debes hacer es asegurarte de disponer de los siguientes requisitos previos.
Requisitos
- Git
- Java™ JDK 11
- Un IDE para desarrollo en Java
- Maven 3.6.2 o posterior
Creación del proyecto Quarkus
Para crear un proyecto Quarkus podemos usar el inicializador disponible en https://code.quarkus.io/ (similar al Initializr de Spring).
Se nos ofrece una lista de extensiones que podemos incluir en nuestra nueva aplicación.
Como la nuestra va a ser una aplicación que meramente exponga un endpoint, elegimos RESTEasy Reactive JSON-B, y luego damos al botón de «Generar». Como herramienta de construcción hemos elegido Maven. Nos descargamos el proyecto, lo descomprimimos en una carpeta y lo importamos en nuestro IDE.
Una vez creado el proyecto, el plugin Quarkus de Maven nos permite gestionar las extensiones. De modo que si, por ejemplo, hubiésemos creado un proyecto plano, podríamos añadir resteasy-jsonb mediante:
mvnw quarkus:add-extension -Dextension=resteasy-jsonb
Puedes ver las extensiones disponibles con mvnw quarkus:list-extensions.
El proyecto generado tendrá esta estructura:
Al examinar el contenido comprobamos que no hay ninguna clase de arranque de la aplicación. Y es que, a diferencia de Spring (que marcaría con @SpringBootApplication la clase principal de la aplicación), en Quarkus no es necesario disponer de una clase arranque semejante (aunque también lo permite, mediante la anotación @QuarkusMain).
Nuestro endpoint de ejemplo está definido en la clase GreetingResource.java: devolverá un saludo cuando se hagan peticiones a /hello.
/code-with-quarkus/src/main/java/org/acme/GreetingResource.java
@Path y @GET son anotaciones del estándar JAX-RS para definir cómo se accede al servicio. En Spring corresponderían a @RequestMapping y @GetMapping, respectivamente. Con @Produces especificamos el tipo de retorno.
Ejecución de la aplicación Quarkus en modo de desarrollo
El modo de desarrollo o dev mode nos permite ejecutar la aplicación y hacer cambios sobre la marcha, que se verán reflejados inmediatamente en la aplicación sin que sea necesario reiniciarla. Quarkus se encarga de recompilarla y recargarla según guardemos nuestros cambios en el código.
Para ejecutar en modo desarrollo hacemos:
mvnw compile quarkus:dev
El modo de desarrollo estará activo mientras esté abierto el terminal. Se obtendrán las librerías necesarias la primera vez que se construya la aplicación y, a continuación, ésta arrancará. Por defecto, la aplicación tiene activo el puerto de depuración 5005. Si no queremos hacer depuración, se puede usar el parámetro -Ddebug=false.
mvnw compile quarkus:dev -Ddebug=false
Para acceder al endpoint navegamos a la URL: https://locall.host/8080/.
Si ahora en el IDE abrimos GreetingResource.java, cambiamos
return "Hello from RESTEasy Reactive";
por
return "hola";
y grabamos, al recargar la página en el navegador veremos incorporado nuestro cambio sin necesidad de reiniciar.
Detendremos la aplicación con CTRL-C-.
Inyección de dependencias en Quarkus
Antes que nada, vamos a parametrizar el mensaje de salida mediante propiedades. En el fichero application.properties definimos las dos siguientes:
/code-with-quarkus/src/main/resources/application.properties
Y las incorporamos en GreetingResource.java mediante @ConfigProperty, una anotación con una función similar a @Value de Spring.
/code-with-quarkus/src/main/java/org/acme/GreetingResource.java
Al recargar el navegador tendremos:
Para que podamos pasar los tests, cambiamos GreetingResourceTest.java, que se generó con el proyecto, para adecuarlo a nuestra nueva salida:
/code-with-quarkus/src/test/java/org/acme/GreetingResourceTest.java
Equivalencias de la inyección de dependencias entre Spring y Quarkus
En general, si conocemos Spring, la forma de organizar nuestro código en Quarkus va a ser muy similar. Esta tabla recoge las equivalencias entre la inyección de dependencias de Spring y las de Quarkus, que sigue el estándar de Jakarta EE (antes conocido como Java EE) y Microprofile:
Spring | CDI / MicroProfile |
---|---|
@Autowired | @Inject |
@Qualifier | @Named |
@Value | @ConfigProperty |
@Component | @Singleton |
@Service | @Singleton |
@Repository | @Singleton |
@Configuration | @ApplicationScoped |
@Bean | @Produces |
@Scope | No hay correspondencia directa. Dependiendo del valor de @Scope, se usará @Singleton, @ApplicationScoped, @SessionScoped, @RequestScoped o @Dependent según se necesite |
@ComponentScan | No hay correspondencia. Quarkus examina todo el classpath en tiempo de construcción. |
Inyección de dependencias con @Inject
Vamos a ver un ejemplo de inyección de dependencias con @Inject. Recurriremos a un servicio para generar el saludo, en vez de hacerlo directamente en GreetingResource:
/code-with-quarkus/src/main/java/org/acme/service/GreetingService.java
Al implementar el servicio, lo anotaremos con @ApplicationScoped para indicar el ámbito en el que opera la instancia:
/code-with-quarkus/src/main/java/org/acme/service/impl/GreetingServiceImpl.java
En Quarkus/MicroProfile disponemos de los siguientes ámbitos:
Anotación | Momento de inicialización | Instancias |
---|---|---|
@ApplicationScoped | Cuando es llamado un método de la clase instanciada | Una por aplicación |
@Singleton | Cuando se inyecta en la clase contenedora | Una por aplicación |
@Dependent | Cuando se inyecta en la clase contenedora | Para cada punto de inyección |
@RequestScoped | Cuando es llamado un método de la clase instanciada | Para cada petición HTTP |
@SessionScoped | Cuando es llamado un método de la clase instanciada | Para cada sesión |
Custome Scope | Depende de la implementación | Depende de la implementación |
Puedes encontrar más información sobre los ámbitos aquí.
Ahora recurrimos a la inyección de dependencias para usar este servicio. Esto lo logramos con @Inject (como hemos visto, similar a @Autowired de Spring).
/code-with-quarkus/src/main/java/org/acme/GreetingResource.java
Si vamos al terminal, comprobaremos que se ha recargado la aplicación con nuestros cambios:
Eventos en Quarkus
Quarkus proporciona una serie de eventos en el ciclo de vida de la inyección de dependencias. A modo de ejemplo, vamos a atender al inicio y a la destrucción de la instancia del servicio mediante @Observe. Los eventos serán StartupEvent y ShutdownEvent.
/code-with-quarkus/src/main/java/org/acme/service/impl/GreetingServiceImpl.java
Cuando iniciamos la aplicación se detecta el evento y se escribe en el terminal:
Tienes más información sobre estos eventos aquí.
Empaquetado de la aplicación Quarkus
Para empaquetar nuestra aplicación Quarkus como ejecutable JAR, ejecutamos el comando:
mvnw package
En el directorio target/ aparecerá el JAR code-with-quarkus-1.0.0-SNAPSHOT-runner.jar. Este JAR no contiene todas las dependencias, sino que las obtiene del directorio target/quarkus-app.
Para obtener un JAR autónomo que contenga todas las dependencias, el llamado uber-JAR, el plugin Maven de Quarkus ofrece la opción quarkus.package.type=uber-jar, que incluiremos en el application.properties, o bien podemos especificarlo en el comando de empaquetado:
mvnw package -Dquarkus.package.type=uber-jar
En ese caso obtendremos dos JAR, el mismo que en el empaquetado normal (renombrado con “original”) y el JAR que incluye todas las librerías y se puede ejecutar autónomamente.
Ejecución del JAR
Probemos a ejecutar nuestro JAR en un terminal con el comando:
java -jar code-with-quarkus-1.0.0-SNAPSHOT-runner.jar
Si queremos publicar la aplicación en otro puerto, por ejemplo 8081, usaremos -Dquarkus.http.port=8081 (el puerto 8080 se reserva para el modo de desarrollo).
Compilación nativa
Por último, en caso de tener instalado en nuestra máquina la herramienta GraalVM, podemos compilar nativamente mediante:
mvnw package -Dnative -Dquarkus.native.container-build=true
Con ello obtendremos las ventajas adicionales en consumo de memoria y tiempo de arranque que expusimos en nuestro primer post.
Conclusión
En esta entrada hemos creado una aplicación muy sencilla en Quarkus para familiarizarnos con esta herramienta y con sus operaciones básicas, en lo que respecta al desarrollo.
Descubre más contenidos interesantes sobre desarrollo de software en nuestro canal de YouTube. ¡Suscríbete!
¿Te gustaría dar el salto al Cloud Computing para acelerar la innovación y reducir costes? En Profile podemos ayudarte. Contacta ahora con nosotros.