1. Introducción
Spring Framework 5.0 agregó compatibilidad exclusiva con Kotlin, lo que facilita el uso de Spring a los desarrolladores de Kotlin. En consecuencia, estos cambios significaron que las integraciones de Google Cloud proporcionadas por Spring Cloud GCP también funcionan sin problemas en Kotlin. En este codelab, verás lo fácil que es comenzar a usar los servicios de Google Cloud en tus aplicaciones de Kotlin.
En este codelab, se explica cómo configurar una aplicación de registro simple en Kotlin que muestra el uso de los servicios de GCP, incluidos Cloud Pub/Sub y Cloud SQL.
Qué compilarás
En este codelab, configurarás una aplicación de Spring Boot de Kotlin que acepta información de los registrantes, la publica en un tema de Cloud Pub/Sub y la persiste en una base de datos de Cloud MySQL.
Qué aprenderás
Cómo realizar la integración con los servicios de Google Cloud en tu aplicación de Spring de Kotlin
Requisitos
- Un proyecto de Google Cloud Platform
- Un navegador, como Chrome o Firefox
¿Cómo usarás este instructivo?
¿Cómo calificarías tu experiencia con la compilación de aplicaciones web con HTML/CSS?
¿Cómo calificarías tu experiencia con el uso de los servicios de Google Cloud Platform?
2. Configuración y requisitos
Configuración del entorno de autoaprendizaje
- Accede a la consola de Cloud y crea un proyecto nuevo o reutiliza uno existente. (Si todavía no tienes una cuenta de Gmail o de G Suite, debes crear una).
Recuerde el ID de proyecto, un nombre único en todos los proyectos de Google Cloud (el nombre anterior ya se encuentra en uso y no lo podrá usar). Se mencionará más adelante en este codelab como PROJECT_ID.
- A continuación, deberás habilitar la facturación en la consola de Cloud para usar los recursos de Google Cloud recursos.
Ejecutar este codelab no debería costar mucho, tal vez nada. Asegúrate de seguir las instrucciones de la sección “Realiza una limpieza”, en la que se aconseja cómo cerrar recursos para que no se te facture más allá de este instructivo. Los usuarios nuevos de Google Cloud son aptos para participar en el programa Prueba gratuita de $300.
Google Cloud Shell
Si bien Google Cloud se puede operar de manera remota desde tu laptop, en este codelab usaremos Google Cloud Shell, un entorno de línea de comandos que se ejecuta en la nube.
Activar Cloud Shell
- En la consola de Cloud, haz clic en Activar Cloud Shell
.
Si nunca ha iniciado Cloud Shell, aparecerá una pantalla intermedia (debajo de la mitad inferior de la página) que describe qué es. Si ese es el caso, haz clic en Continuar (y no volverás a verlo). Así es como se ve la pantalla única:
El aprovisionamiento y la conexión a Cloud Shell solo tomará unos minutos.
Esta máquina virtual está cargada con todas las herramientas de desarrollo que necesitarás. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que permite mejorar considerablemente el rendimiento de la red y la autenticación. Gran parte de tu trabajo en este codelab, si no todo, se puede hacer simplemente con un navegador o tu Chromebook.
Una vez conectado a Cloud Shell, debería ver que ya se autenticó y que el proyecto ya se configuró con tu ID del proyecto.
- En Cloud Shell, ejecuta el siguiente comando para confirmar que está autenticado:
gcloud auth list
Resultado del comando
Credentialed Accounts
ACTIVE ACCOUNT
* <my_account>@<my_domain.com>
To set the active account, run:
$ gcloud config set account `ACCOUNT`
gcloud config list project
Resultado del comando
[core] project = <PROJECT_ID>
De lo contrario, puedes configurarlo con el siguiente comando:
gcloud config set project <PROJECT_ID>
Resultado del comando
Updated property [core/project].
3. Aprovisiona recursos de Pub/Sub
Primero, deberemos configurar un tema y una suscripción de Cloud Pub/Sub. En esta aplicación, publicaremos información de registro en un tema de Pub/Sub. Luego, la información se leerá desde este tema y se conservará en una base de datos.
En este instructivo, usaremos Cloud Shell para aprovisionar nuestros recursos. Ten en cuenta que también se pueden configurar recursos de Pub/Sub a través de la sección Cloud Pub/Sub en la consola de Google Cloud.
En la terminal de Cloud Shell, primero habilita la API de Pub/Sub.
$ gcloud services enable pubsub.googleapis.com
A continuación, crearemos un tema de Pub/Sub llamado registrations para esta aplicación. La información de registro enviada a través de la aplicación se publicará en este tema.
$ gcloud pubsub topics create registrations
Por último, crea una suscripción para el tema. Una suscripción a Pub/Sub te permite recibir mensajes de un tema.
$ gcloud pubsub subscriptions create registrations-sub --topic=registrations
Ya terminaste de crear un tema y una suscripción de Cloud Pub/Sub para tu aplicación.
4. Crea una instancia y una base de datos de Cloud SQL (MySQL)
Para nuestra aplicación de ejemplo, también debemos configurar una instancia de base de datos para almacenar la información de los registrantes. Este paso también se basará en la terminal de Cloud Shell para aprovisionar recursos de Cloud SQL. Ten en cuenta que también puedes ver y configurar tus instancias de Cloud SQL a través de la consola de Google Cloud.
Primero, habilita la API de Cloud SQL Admin.
$ gcloud services enable sqladmin.googleapis.com
A continuación, aprovisionaremos una instancia de Cloud SQL (MySQL). Este comando puede tardar un poco.
$ gcloud sql instances create codelab-instance --region=us-east1
Después de crear correctamente tu instancia de Cloud SQL, crea una base de datos nueva en tu instancia llamada registrants.
$ gcloud sql databases create registrants --instance codelab-instance
Ya completaste la configuración de la instancia y la base de datos de Cloud SQL para tu aplicación.
5. Inicializa una aplicación de Spring Boot
Ahora, ya podemos comenzar a escribir la aplicación. En los siguientes pasos, se seguirá usando Cloud Shell que se describió en los pasos de configuración.
Primero, usaremos Initializr para generar el código de estructura del proyecto. En la ventana de Cloud Shell, ejecuta el siguiente comando:
$ cd ~
$ curl https://start.spring.io/starter.tgz \
-d language=kotlin \
-d bootVersion=2.4.0 \
-d dependencies=web,data-jpa,integration,cloud-gcp-pubsub,thymeleaf \
-d baseDir=registrations-codelab | tar -xzvf -
$ cd registrations-codelab
Este comando genera una configuración inicial del proyecto de Maven, así como código de scaffolding para tu aplicación en el directorio registrations-codelab/. En las siguientes secciones, se describen las ediciones de código necesarias para producir una aplicación que funcione.
Editor de código de Cloud Shell
La forma más sencilla de comenzar a modificar y ver código en el entorno de Cloud Shell es usar el Editor de código de Cloud Shell integrado.
Una vez que hayas abierto una instancia de Cloud Shell, haz clic en el ícono de lápiz para abrir el editor de código. El editor debería permitirte modificar directamente los archivos del proyecto que produce Initialzr.

6. Configuración de la base de datos
Primero, configura tu aplicación para que se pueda conectar a la base de datos de Cloud MySQL que configuraste. Las bibliotecas de Spring Cloud GCP ofrecen un Cloud MySQL starter que proporciona las dependencias necesarias para conectarse a una instancia de Cloud MySQL.
Agrega la dependencia spring-cloud-gcp-starter-sql-mysql al archivo pom.xml del proyecto:
registrations-codelab/pom.xml
...
<dependencies>
... Other dependencies above ...
<!-- Add the MySQL starter to the list of dependencies -->
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>spring-cloud-gcp-starter-sql-mysql</artifactId>
</dependency>
</dependencies>
Además, debes modificar el archivo de configuración application.properties para describir la configuración de tu base de datos. Copia las siguientes propiedades en tu archivo application.properties.
Busca el nombre de conexión de la instancia a tu base de datos:
$ gcloud sql instances describe codelab-instance \ --format 'value(connectionName)'
El resultado de este comando se usará en el archivo application.properties para configurar la información de conexión.
src/main/resources/application.properties
# Modify this property using the output from the previous command line. spring.cloud.gcp.sql.instance-connection-name=INSTANCE_CONNECTION_NAME # Your database name spring.cloud.gcp.sql.database-name=registrants # So app starts despite "table already exists" errors. spring.datasource.continue-on-error=true # Enforces database initialization spring.datasource.initialization-mode=always # Cloud SQL (MySQL) only supports InnoDB, not MyISAM spring.jpa.database-platform=org.hibernate.dialect.MySQL55Dialect spring.jpa.hibernate.ddl-auto=create-drop # This is used if you want to connect to a different database instance # user other than root; not used in codelab. # spring.datasource.username=root # This is used to specify the password of the database user; # not used in codelab. # spring.datasource.password=password
La única propiedad que debes modificar es el nombre de conexión de la instancia. Este valor debe tener el formato de un valor separado por dos puntos con la siguiente forma: YOUR_GCP_PROJECT_ID:REGION:DATABASE_INSTANCE_NAME.
7. Cómo crear el contenido estático
Primero, crearemos el frontend de nuestra aplicación. La aplicación debe tener un formulario que permita registrar personas y una vista que muestre a todos los usuarios registrados correctamente.
Para la página principal, crea un index.html que contenga el formulario de registro.
src/main/resources/static/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Registration Sample Application</title>
</head>
<body>
<h1>Registration</h1>
<div>
<nav>
<a href="/">Home</a><br>
<a href="/registrants">Registered People</a><br>
</nav>
<p>
This is a demo registration application which sends user information to a Pub/Sub topic and
persists it into a MySQL database.
</p>
<h2>Register Person</h2>
<div>
<form action="/registerPerson" method="post">
First Name: <input type="text" name="firstName" />
Last Name: <input type="text" name="lastName" />
Email: <input type="text" name="email" />
<input type="submit" value="Submit"/>
</form>
</div>
</div>
</body>
</html>
A continuación, crearemos una plantilla de Thymeleaf llamada registrants.html para mostrar los usuarios registrados. Thymeleaf es un framework de plantillas que usamos para construir y publicar HTML creado de forma dinámica. Verás que la plantilla se parece a HTML, excepto que tiene algunos elementos de Markdown adicionales para controlar el contenido dinámico. Esta plantilla acepta un solo parámetro llamado personsList que contiene a todos los usuarios que se registraron a través de la aplicación.
src/main/resources/templates/registrants.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Registrants List</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h1>Registrants List</h1>
<p>
This page displays all the people who were registered through the Pub/Sub topic.
All results are retrieved from the MySQL database.
</p>
<table border="1">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
<tr th:each="person : ${personsList}">
<td>[[${person.firstName}]]</td>
<td>[[${person.lastName}]]</td>
<td>[[${person.email}]]</td>
</tr>
</table>
</body>
</html>
En este punto, puedes verificar que se esté publicando el contenido estático.
Compila y ejecuta la app con Maven:
$ ./mvnw spring-boot:run
Haz clic en el botón de vista previa en la ventana de Cloud Shell y verifica que se renderice la página principal. Sin embargo, ninguna de las funciones de la IU funcionará porque falta un controlador web. Esto se agregará en el siguiente paso.

Después de obtener una vista previa de la aplicación, presiona CTRL+C para cerrarla.
8. Envía a los registrantes a un tema de Pub/Sub
En este paso, implementaremos la función en la que los usuarios que se registren a través del formulario web se publicarán en un tema de Cloud Pub/Sub.
Agrega las clases de datos
Primero, crearemos algunas clases de datos de Kotlin. Estas serán nuestras entidades de JPA y también actuarán como nuestra representación intermedia de los registrantes enviados a través del formulario.
En el paquete de demostración, agrega dos archivos nuevos: una clase Person y un PersonRepository de Spring Data. Estas dos clases nos permitirán almacenar y recuperar fácilmente las entradas de registro de nuestra base de datos de MySQL con Spring Data JPA.
src/main/kotlin/com/example/demo/Person.kt
package com.example.demo
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.Id
@Entity
data class Person(
val firstName: String,
val lastName: String,
val email: String,
@Id @GeneratedValue
var id: Long? = 0)
src/main/kotlin/com/example/demo/PersonRepository.kt
package com.example.demo
import org.springframework.data.repository.CrudRepository
interface PersonRepository : CrudRepository<Person, Long>
Agrega el controlador web
A continuación, crearemos una clase Controller que procese a los usuarios que se registren en el formulario y envíe la información al tema de Cloud Pub/Sub que creaste anteriormente. Este controlador crea dos extremos:
/registerPerson: Es el extremo POST en el que se envía la información del registrante y, luego, se envía al tema de Pub/Sub. En la funciónregisterPerson(..), la información del registrante se envía al tema de Pub/Sub conPubSubTemplate, una clase de conveniencia de las integraciones de Spring Cloud GCP Pub/Sub que minimiza el código estándar necesario para comenzar a interactuar con Cloud Pub/Sub./registrants: Muestra todos los usuarios registrados correctamente en la base de datos. Esta información se recupera de la instancia de MySQL con el repositorio de Spring Data que creamos en el paso anterior.
Crea la siguiente clase Controller en el paquete de demostración:
src/main/kotlin/com/example/demo/Controller.kt
package com.example.demo
import com.google.cloud.spring.pubsub.core.PubSubTemplate
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.servlet.ModelAndView
import org.springframework.web.servlet.view.RedirectView
@RestController
class Controller(val pubSubTemplate: PubSubTemplate, val personRepository: PersonRepository) {
// The Pub/Sub topic name created earlier.
val REGISTRATION_TOPIC = "registrations"
@PostMapping("/registerPerson")
fun registerPerson(
@RequestParam("firstName") firstName: String,
@RequestParam("lastName") lastName: String,
@RequestParam("email") email: String): RedirectView {
pubSubTemplate.publish(
REGISTRATION_TOPIC,
Person(firstName, lastName, email))
return RedirectView("/")
}
@GetMapping("/registrants")
fun getRegistrants(): ModelAndView {
val personsList = personRepository.findAll().toList()
return ModelAndView("registrants", mapOf("personsList" to personsList))
}
}
El controlador lee la información del registrante enviada a través del formulario web y, luego, publica la información en el tema de Pub/Sub.
Cómo agregar el bean de Object Mapper de JSON
Es posible que hayas notado en el controlador que publicamos un objeto Person en el tema de Pub/Sub y no una cadena. Esto es posible porque aprovechamos la compatibilidad de Spring Cloud GCP con cargas útiles de JSON personalizadas para enviarlas a temas. Las bibliotecas te permiten serializar objetos en JSON, enviar cargas útiles de JSON a un tema y deserializar la carga útil cuando se recibe.
Para aprovechar esta función, debemos agregar un bean ObjectMapper al contexto de tu aplicación. Este bean ObjectMapper se usará para serializar objetos a JSON y desde JSON cuando tu aplicación envíe y reciba mensajes. En la clase DemoApplication.kt, agrega el Spring bean JacksonPubSubMessageConverter:
src/main/kotlin/com/example/demo/DemoApplication.kt
package com.example.demo
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
// new imports to add
import org.springframework.context.annotation.Bean
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.cloud.spring.pubsub.support.converter.JacksonPubSubMessageConverter
@SpringBootApplication
class DemoApplication {
// This bean enables serialization/deserialization of
// Java objects to JSON for Pub/Sub payloads
@Bean
fun jacksonPubSubMessageConverter(objectMapper: ObjectMapper) =
JacksonPubSubMessageConverter(objectMapper)
}
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
En este punto, puedes intentar ejecutar la aplicación nuevamente con el siguiente comando:
$ ./mvnw spring-boot:run
Desde el formulario web de la página principal, la aplicación ahora enviará la información al tema de Pub/Sub que creaste. Sin embargo, aún no hace nada útil porque todavía necesitamos leer desde ese tema de Pub/Sub. Esto se logra en el siguiente paso.
9. Lectura de los registros del tema de Pub/Sub
En el último paso, procesaremos la información de los registrantes del tema de Pub/Sub y la persistiremos en la base de datos de Cloud MySQL. Esto completará la aplicación, lo que te permitirá enviar nuevos registros a través del formulario y ver todos los usuarios registrados a través del extremo /registrants.
Esta aplicación aprovechará Spring Integration, que ofrece muchas abstracciones convenientes para trabajar con la mensajería. Agregaremos un PubSubInboundChannelAdapter para poder leer mensajes del tema de Pub/Sub y colocarlos en el pubsubInputChannel para su procesamiento posterior. Luego, configuraremos la función messageReceiver con @ServiceActivator para que se invoque con los mensajes que llegan a pubsubInputChannel.
src/main/kotlin/com/example/demo/DemoApplication.kt
package com.example.demo
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.cloud.gcp.pubsub.support.converter.JacksonPubSubMessageConverter
// new imports to add
import com.google.cloud.spring.pubsub.core.PubSubTemplate
import com.google.cloud.spring.pubsub.integration.AckMode
import com.google.cloud.spring.pubsub.integration.inbound.PubSubInboundChannelAdapter
import com.google.cloud.spring.pubsub.support.BasicAcknowledgeablePubsubMessage
import com.google.cloud.spring.pubsub.support.GcpPubSubHeaders
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.integration.annotation.ServiceActivator
import org.springframework.integration.channel.DirectChannel
import org.springframework.messaging.MessageChannel
import org.springframework.messaging.handler.annotation.Header
@SpringBootApplication
class DemoApplication {
private val REGISTRANT_SUBSCRIPTION = "registrations-sub"
@Autowired
private lateinit var personRepository: PersonRepository
// New Spring Beans to add
@Bean
fun pubsubInputChannel() = DirectChannel()
@Bean
fun messageChannelAdapter(
@Qualifier("pubsubInputChannel") inputChannel: MessageChannel,
pubSubTemplate: PubSubTemplate): PubSubInboundChannelAdapter {
val adapter = PubSubInboundChannelAdapter(
pubSubTemplate, REGISTRANT_SUBSCRIPTION)
adapter.outputChannel = inputChannel
adapter.ackMode = AckMode.MANUAL
adapter.payloadType = Person::class.java
return adapter
}
@ServiceActivator(inputChannel = "pubsubInputChannel")
fun messageReceiver(
payload: Person,
@Header(GcpPubSubHeaders.ORIGINAL_MESSAGE) message: BasicAcknowledgeablePubsubMessage) {
personRepository.save(payload)
print("Message arrived! Payload: $payload")
message.ack()
}
// ObjectMapper bean from previous step
@Bean
fun jacksonPubSubMessageConverter(objectMapper: ObjectMapper) = JacksonPubSubMessageConverter(objectMapper)
}
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
En este punto, completaste la configuración de la aplicación. Para verificar que la app funcione correctamente, ejecuta el siguiente comando:
$ ./mvnw spring-boot:run
Vuelve a hacer clic en el botón Vista previa y trata de registrar un usuario completando el formulario y enviándolo.

Haz clic en el vínculo Registered People para verificar que el nuevo usuario registrado aparezca en la tabla.

¡Felicitaciones! Ya terminaste. Presiona CTRL+C en la ventana de la terminal para finalizar la aplicación.
10. Limpieza
Para limpiar tu entorno, debes borrar el tema de Pub/Sub y la instancia de Cloud MySQL que creaste.
Cómo borrar la instancia de Cloud MySQL
$ gcloud sql instances delete codelab-instance
Borra los recursos de Pub/Sub
$ gcloud pubsub subscriptions delete registrations-sub $ gcloud pubsub topics delete registrations
11. ¡Felicitaciones!
Ahora completaste la escritura de una aplicación de Spring Kotlin que se integra con Cloud Pub/Sub y Cloud SQL (MySQL).
Más información
- Proyecto Spring on GCP: http://cloud.spring.io/spring-cloud-gcp/
- Repositorio de GitHub de Spring en GCP: https://github.com/GoogleCloudPlatform/spring-cloud-gcp
- Java en Google Cloud: https://cloud.google.com/java/
- Ejemplos de aplicaciones de Kotlin que usan GCP: https://github.com/GoogleCloudPlatform/spring-cloud-gcp/tree/master/spring-cloud-gcp-kotlin-samples
Licencia
Este trabajo cuenta con una licencia Atribución 2.0 Genérica de Creative Commons.