Usa contenedores para el desarrollo con Java
Prerrequisitos
Sigue los pasos para contenerizar tu aplicación en Contenerizar tu aplicación.
Descripción general
En esta sección, configurarás un entorno de desarrollo local para la aplicación que contenerizaste en la sección anterior. Esto incluye:
- Agregar una base de datos local y persistir datos
- Crear un contenedor de desarrollo para conectar un depurador
- Configurar Compose para actualizar automáticamente tus servicios de Compose en ejecución a medida que editas y guardas tu código
Agregar una base de datos local y persistir datos
Puedes usar contenedores para configurar servicios locales, como una base de datos. En esta sección, actualizarás el archivo docker-compose.yaml para definir un servicio de base de datos y un volumen para persistir los datos. Además, esta aplicación en particular utiliza una propiedad del sistema para definir el tipo de base de datos, por lo que tendrás que actualizar el Dockerfile para pasar la propiedad del sistema al iniciar la aplicación.
En el directorio del repositorio clonado, abre el archivo docker-compose.yaml en un IDE o editor de texto. Tu archivo de Compose tiene un servicio de base de datos de ejemplo, pero requerirá algunos cambios para tu aplicación específica.
En el archivo docker-compose.yaml, debes hacer lo siguiente:
- Descomentar todas las instrucciones de la base de datos. Ahora usarás un servicio de base de datos en lugar de almacenamiento local para los datos.
- Eliminar el elemento de nivel superior
secretsasí como el elemento dentro del serviciodb. Este ejemplo utiliza la variable de entorno para la contraseña en lugar de secretos. - Eliminar el elemento
userdel serviciodb. Este ejemplo especifica el usuario en la variable de entorno. - Actualizar las variables de entorno de la base de datos. Estas están definidas por la imagen de Postgres. Para más detalles, consulta la Imagen oficial de Docker de Postgres.
- Actualizar la prueba de comprobación de estado (healthcheck) para el servicio
dby especificar el usuario. Por defecto, la comprobación de estado utiliza el usuario root en lugar del usuariopetclinicque definiste. - Agregar la URL de la base de datos como una variable de entorno en el servicio
server. Esto sobrescribe el valor predeterminado definido enspring-petclinic/src/main/resources/application-postgres.properties.
A continuación se muestra el archivo docker-compose.yaml actualizado. Se han eliminado todos los comentarios.
services:
server:
build:
context: .
ports:
- 8080:8080
depends_on:
db:
condition: service_healthy
environment:
- POSTGRES_URL=jdbc:postgresql://db:5432/petclinic
db:
image: postgres:18
restart: always
volumes:
- db-data:/var/lib/postgresql
environment:
- POSTGRES_DB=petclinic
- POSTGRES_USER=petclinic
- POSTGRES_PASSWORD=petclinic
ports:
- 5432:5432
healthcheck:
test: ["CMD", "pg_isready", "-U", "petclinic"]
interval: 10s
timeout: 5s
retries: 5
volumes:
db-data:Abre el Dockerfile en un IDE o editor de texto. En la instrucción ENTRYPOINT,
actualiza la instrucción para pasar la propiedad del sistema como se especifica en el
archivo spring-petclinic/src/resources/db/postgres/petclinic_db_setup_postgres.txt.
- ENTRYPOINT [ "java", "org.springframework.boot.loader.launch.JarLauncher" ]
+ ENTRYPOINT [ "java", "-Dspring.profiles.active=postgres", "org.springframework.boot.loader.launch.JarLauncher" ]
Guarda y cierra todos los archivos.
Ahora, ejecuta el siguiente comando docker compose up para iniciar tu aplicación.
$ docker compose up --build
Abre un navegador y visita la aplicación en http://localhost:8080. Deberías ver una aplicación sencilla para una clínica veterinaria (pet clinic). Explora la aplicación. Ve a Veterinarians y verifica que la aplicación esté conectada a la base de datos comprobando que puede listar a los veterinarios.
En la terminal, presiona ctrl+c para detener la aplicación.
Dockerfile para desarrollo
El Dockerfile que tienes ahora es ideal para una imagen de producción pequeña y segura con solo los componentes necesarios para ejecutar la aplicación. Al desarrollar, es posible que quieras una imagen diferente que tenga un entorno diferente.
Por ejemplo, en la imagen de desarrollo puedes querer configurar la imagen para iniciar la aplicación de modo que puedas conectar un depurador al proceso de Java en ejecución.
En lugar de gestionar múltiples Dockerfiles, puedes agregar una nueva etapa (stage). Tu Dockerfile puede producir entonces tanto una imagen final lista para producción como una imagen de desarrollo.
Reemplaza el contenido de tu Dockerfile con el siguiente.
# syntax=docker/dockerfile:1
FROM eclipse-temurin:21-jdk-jammy as deps
WORKDIR /build
COPY --chmod=0755 mvnw mvnw
COPY .mvn/ .mvn/
RUN --mount=type=bind,source=pom.xml,target=pom.xml \
--mount=type=cache,target=/root/.m2 ./mvnw dependency:go-offline -DskipTests
FROM deps as package
WORKDIR /build
COPY ./src src/
RUN --mount=type=bind,source=pom.xml,target=pom.xml \
--mount=type=cache,target=/root/.m2 \
./mvnw package -DskipTests && \
mv target/$(./mvnw help:evaluate -Dexpression=project.artifactId -q -DforceStdout)-$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout).jar target/app.jar
FROM package as extract
WORKDIR /build
RUN java -Djarmode=layertools -jar target/app.jar extract --destination target/extracted
FROM extract as development
WORKDIR /build
RUN cp -r /build/target/extracted/dependencies/. ./
RUN cp -r /build/target/extracted/spring-boot-loader/. ./
RUN cp -r /build/target/extracted/snapshot-dependencies/. ./
RUN cp -r /build/target/extracted/application/. ./
ENV JAVA_TOOL_OPTIONS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000
CMD [ "java", "-Dspring.profiles.active=postgres", "org.springframework.boot.loader.launch.JarLauncher" ]
FROM eclipse-temurin:21-jre-jammy AS final
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser
USER appuser
COPY --from=extract build/target/extracted/dependencies/ ./
COPY --from=extract build/target/extracted/spring-boot-loader/ ./
COPY --from=extract build/target/extracted/snapshot-dependencies/ ./
COPY --from=extract build/target/extracted/application/ ./
EXPOSE 8080
ENTRYPOINT [ "java", "-Dspring.profiles.active=postgres", "org.springframework.boot.loader.launch.JarLauncher" ]Guarda y cierra el Dockerfile.
En el Dockerfile has agregado una nueva etapa llamada development basada en la etapa extract. En esta etapa, copias los archivos extraídos a un directorio común y luego ejecutas un comando para iniciar la aplicación. En el comando, expones el puerto 8000 y declaras la configuración de depuración para la JVM para que puedas conectar un depurador.
Usar Compose para desarrollar localmente
El archivo actual de Compose no inicia tu contenedor de desarrollo. Para hacer eso, debes actualizar tu archivo de Compose para apuntar a la etapa development. Además, actualiza el mapeo de puertos del servicio server para proporcionar acceso al depurador.
Abre el archivo docker-compose.yaml y agrega las siguientes instrucciones.
services:
server:
build:
context: .
target: development
ports:
- 8080:8080
- 8000:8000
depends_on:
db:
condition: service_healthy
environment:
- POSTGRES_URL=jdbc:postgresql://db:5432/petclinic
db:
image: postgres:18
restart: always
volumes:
- db-data:/var/lib/postgresql
environment:
- POSTGRES_DB=petclinic
- POSTGRES_USER=petclinic
- POSTGRES_PASSWORD=petclinic
ports:
- 5432:5432
healthcheck:
test: ["CMD", "pg_isready", "-U", "petclinic"]
interval: 10s
timeout: 5s
retries: 5
volumes:
db-data:Ahora, inicia tu aplicación para confirmar que está funcionando.
$ docker compose up --build
Por último, prueba tu punto de acceso (endpoint) de API. Ejecuta el siguiente comando curl:
$ curl --request GET \
--url http://localhost:8080/vets \
--header 'content-type: application/json'
Deberías recibir la siguiente respuesta:
{
"vetList": [
{
"id": 1,
"firstName": "James",
"lastName": "Carter",
"specialties": [],
"nrOfSpecialties": 0,
"new": false
},
{
"id": 2,
"firstName": "Helen",
"lastName": "Leary",
"specialties": [{ "id": 1, "name": "radiology", "new": false }],
"nrOfSpecialties": 1,
"new": false
},
{
"id": 3,
"firstName": "Linda",
"lastName": "Douglas",
"specialties": [
{ "id": 3, "name": "dentistry", "new": false },
{ "id": 2, "name": "surgery", "new": false }
],
"nrOfSpecialties": 2,
"new": false
},
{
"id": 4,
"firstName": "Rafael",
"lastName": "Ortega",
"specialties": [{ "id": 2, "name": "surgery", "new": false }],
"nrOfSpecialties": 1,
"new": false
},
{
"id": 5,
"firstName": "Henry",
"lastName": "Stevens",
"specialties": [{ "id": 1, "name": "radiology", "new": false }],
"nrOfSpecialties": 1,
"new": false
},
{
"id": 6,
"firstName": "Sharon",
"lastName": "Jenkins",
"specialties": [],
"nrOfSpecialties": 0,
"new": false
}
]
}Conectar un depurador
Usarás el depurador que viene con IntelliJ IDEA. Puedes usar la versión community de este IDE. Abre tu proyecto en IntelliJ IDEA, ve al menú Run y luego a Edit Configuration. Agrega una nueva configuración de tipo Remote JVM Debug similar a la siguiente:

Establece un punto de interrupción (breakpoint).
Abre src/main/java/org/springframework/samples/petclinic/vet/VetController.java y agrega un punto de interrupción dentro de la función showResourcesVetList.
Para iniciar tu sesión de depuración, selecciona el menú Run y luego Debug NameOfYourConfiguration.

Ahora deberías ver la conexión en los logs de tu aplicación de Compose.

Ahora puedes llamar al endpoint del servidor.
$ curl --request GET --url http://localhost:8080/vets
Deberías haber visto que el código se detiene en la línea marcada y ahora puedes usar el depurador igual que lo harías normalmente. También puedes inspeccionar y observar variables, establecer puntos de interrupción condicionales, ver trazas de la pila (stack traces) y hacer muchas otras cosas.

Presiona ctrl+c en la terminal para detener tu aplicación.
Actualizar servicios automáticamente
Usa Compose Watch para actualizar automáticamente tus servicios de Compose en ejecución a medida que editas y guardas tu código. Para obtener más detalles sobre Compose Watch, consulta Usar Compose Watch.
Abre tu archivo docker-compose.yaml en un IDE o editor de texto y luego agrega las
instrucciones de Compose Watch. A continuación se muestra el archivo docker-compose.yaml
actualizado.
services:
server:
build:
context: .
target: development
ports:
- 8080:8080
- 8000:8000
depends_on:
db:
condition: service_healthy
environment:
- POSTGRES_URL=jdbc:postgresql://db:5432/petclinic
develop:
watch:
- action: rebuild
path: .
db:
image: postgres:18
restart: always
volumes:
- db-data:/var/lib/postgresql
environment:
- POSTGRES_DB=petclinic
- POSTGRES_USER=petclinic
- POSTGRES_PASSWORD=petclinic
ports:
- 5432:5432
healthcheck:
test: ["CMD", "pg_isready", "-U", "petclinic"]
interval: 10s
timeout: 5s
retries: 5
volumes:
db-data:Ejecuta el siguiente comando para ejecutar tu aplicación con Compose Watch.
$ docker compose watch
Abre un navegador web y visita la aplicación en http://localhost:8080. Deberías ver la página de inicio de Spring Pet Clinic.
Cualquier cambio en los archivos fuente de la aplicación en tu máquina local se reflejará automáticamente en el contenedor en ejecución.
Abre spring-petclinic/src/main/resources/templates/fragments/layout.html en un IDE o editor de texto y actualiza el texto de navegación de Home agregando un signo de exclamación.
- <li th:replace="~{::menuItem ('/','home','home page','home','Home')}">
+ <li th:replace="~{::menuItem ('/','home','home page','home','Home!')}">
Guarda los cambios en layout.html y luego puedes continuar desarrollando mientras el contenedor se reconstruye automáticamente.
Una vez que el contenedor se haya reconstruido y esté ejecutándose, refresca http://localhost:8080 y verifica que ahora aparece Home! en el menú.
Presiona ctrl+c en la terminal para detener Compose Watch.
Resumen
En esta sección, analizaste el funcionamiento de una base de datos local y la persistencia de datos. También creaste una imagen de desarrollo que contiene el JDK y te permite conectar un depurador. Finalmente, configuraste tu archivo de Compose para exponer el puerto de depuración y configuraste Compose Watch para recargar en vivo tus cambios.
Información relacionada:
Próximos pasos
En la siguiente sección, verás cómo ejecutar pruebas unitarias en Docker.