Skip to content

Latest commit

 

History

History
406 lines (265 loc) · 14.5 KB

File metadata and controls

406 lines (265 loc) · 14.5 KB

🍃 learning-spring-boot

Três ambientes Spring Boot isolados, um único repositório.

Conteúdo de aula, pesquisa pessoal e o projeto da faculdade — cada um no seu container, nas suas portas, rodando lado a lado sem nunca colidir.

Java Spring Boot Maven Docker Dev Containers

English 🇺🇸 · Português


📚 Índice


🎯 Por que três ambientes

Código de estudo, exercícios e projeto avaliado têm necessidades diferentes. Misturar tudo num só lugar afoga o projeto entregue em arquivos de rascunho, e um experimento quebrado pode derrubar justamente o que vai ser avaliado.

Por isso cada um vive no seu próprio container:

Pasta O que vai aqui Ferramental
🎓 class-content/ Conteúdo teórico e exercícios passados mínimo
🧪 playground/ Pesquisa pessoal, fora da faculdade completo
🚀 projects/ O projeto avaliado da faculdade completo

Mínimo é JDK 21, Maven, git e curl — nada além disso, para que nada desvie a atenção do exercício.

Completo acrescenta clientes de banco (psql, mysql, redis-cli), ferramentas de rede (netcat, dig, ping), jq, httpie, ripgrep, tree, graphviz e editores.


📂 Estrutura do repositório

learning-spring-boot/
│
├── 🎓 class-content/          Ambiente mínimo
│   ├── theory/                Material teórico e de referência
│   └── exercises/             Um projeto Maven por exercício
│       └── challange-01/      API de Jogos — veja o README dela
│
├── 🧪 playground/             Ambiente completo — experimentos descartáveis
│
├── 🚀 projects/               Ambiente completo — o projeto avaliado
│
├── 🐳 docker/
│   ├── Dockerfile             Multi-stage: base · full · build · production
│   ├── docker-compose.yml     Os três serviços de dev + o de produção
│   └── docker-compose.override.yml   Reserva opcional de GPU NVIDIA
│
└── ⚙️  .devcontainer/
    ├── class-content/         Uma definição de devcontainer por ambiente
    ├── playground/
    └── projects/

Cada pasta tem também um CLAUDE.md com as instruções específicas dela.


🔌 Portas

Ambiente App Depurador Stage do Docker
🎓 class-content 8082 5005 base
🚀 projects 8083 5007 full
📦 projects (imagem de produção) 8084 production
🧪 playground 8085 5006 full

⚠️ Portas do host x portas do container

Essas são as portas da sua máquina. Dentro de todos os containers o app sempre escuta na 8081 e o depurador na 5005. Nada no código Java ou no application.properties menciona 8082/8083/8085 — o remapeamento existe só para os três poderem rodar ao mesmo tempo, e está definido apenas no docker-compose.yml.

O único lugar onde a porta do host importa é o front-end: o navegador roda na sua máquina, então as chamadas fetch e o CORS da API precisam usar a porta do host, não a 8081.


🛠 Requisitos

  • 🐳 Docker — Docker Desktop no Windows ou macOS, Docker Engine no Linux
  • 💻 VS Code com a extensão Dev Containers para o fluxo de devcontainer

Nada mais. Java e Maven ficam dentro dos containers — não é preciso ter JDK instalado na máquina.


🚀 Começando

Opção A — VS Code Dev Containers (recomendado)

  1. Abra o repositório no VS Code
  2. Execute Dev Containers: Reopen in Container na paleta de comandos (F1)
  3. Escolha o ambiente em que quer trabalhar
  4. No terminal do container, suba o app:
mvn spring-boot:run

As extensões de Java e o cache do Maven já vêm configurados.

Opção B — Docker Compose direto

# Sobe os três ambientes de desenvolvimento
docker compose -f docker/docker-compose.yml up -d

# Abre um shell em um deles
docker exec -it learning-spring-boot-projects bash

# Depois, lá dentro:
mvn spring-boot:run

💡 Nada sobe sozinho

Os containers ficam ociosos de propósito — não sobem aplicação sozinhos, então você decide quando algo inicia. Eles não têm healthcheck e aparecem apenas como Up; só o serviço de produção é monitorado.

Fechar a janela do devcontainer também deixa os containers rodando, para não atrapalhar os outros ambientes. Para derrubar tudo: docker compose -f docker/docker-compose.yml down.


🧰 Comandos do dia a dia

Rode estes dentro do container, na pasta que tem o pom.xml:

Comando O que faz
mvn spring-boot:run Sobe o app na porta 8081 (dentro do container)
mvn test Roda os testes unitários
mvn verify Testes + lint e cobertura configurados
mvn clean package Gera o jar em target/
mvn dependency:tree Mostra o grafo de dependências
jshell REPL de Java — testar ideias sem criar projeto

Da sua máquina:

Comando O que faz
docker compose -f docker/docker-compose.yml up -d Sobe todos os ambientes
docker compose -f docker/docker-compose.yml down Derruba todos
docker compose -f docker/docker-compose.yml build --no-cache Reconstrói as imagens do zero
docker compose -f docker/docker-compose.yml logs -f projects Acompanha o log de um serviço
docker ps Estado e saúde de cada container

🐞 Depuração

Não é preciso instalar nada — o agente JDWP faz parte da JVM, então a depuração remota funciona em todos os ambientes, inclusive no mínimo. Diferente de Python, onde você precisaria de um pip install debugpy antes.

Suba o app com a flag de depuração:

mvn spring-boot:run -Dspring-boot.run.jvmArguments="$JAVA_DEBUG_OPTS"

$JAVA_DEBUG_OPTS já vem definida na imagem e abre a porta 5005 dentro do container. Depois conecte a IDE na porta de debug do host daquela pasta:

// .vscode/launch.json — ajuste a porta conforme o ambiente
{
  "type": "java",
  "name": "Attach to container",
  "request": "attach",
  "hostName": "localhost",
  "port": 5007        // 5005 class-content · 5006 playground · 5007 projects
}

Disponíveis também em qualquer container, direto do JDK:

Ferramenta Para que serve
jdb Depurador de linha de comando
jstack <pid> Dump de threads — encontrar deadlocks
jmap <pid> Dump de heap — investigar vazamento de memória
jcmd <pid> Thread.print Comandos ao vivo na JVM
jfr Java Flight Recorder — profiling

✅ Testes e qualidade de código

Java não tem equivalente instalável no sistema para pytest ou pylint. As ferramentas são dependências e plugins do Maven declarados por projeto, o que significa que adicionar uma delas nunca exige reconstruir a imagem Docker.

Papel Ferramenta Java Equivalente em Python Declarada em
Executor de testes JUnit 5 pytest <dependencies>
Assertions / mocks AssertJ, Mockito vem no starter
Teste de controllers MockMvc vem no starter
Bancos reais em teste Testcontainers <dependencies>
Estilo de código Checkstyle pylint <plugins>
Análise estática SpotBugs, PMD pylint <plugins>
Cobertura JaCoCo coverage.py <plugins>

JUnit, AssertJ, Mockito e MockMvc chegam todos de uma vez por uma única dependência:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>
mvn test      # só os testes unitários
mvn verify    # testes + lint + cobertura

📦 Imagem de produção

A pasta projects/ também pode ser construída como uma imagem de runtime enxuta, para quando o projeto da faculdade precisar ser entregue ou publicado:

  • 🪶 Só JRE, sobre Alpine — sem Maven, sem git, sem compilador
  • 🔒 Usuário sem privilégios de root
  • Inicia o jar direto pelo ENTRYPOINT, sem compilar em tempo de execução
  • 🧠 -XX:MaxRAMPercentage=75.0, para a JVM respeitar o limite de memória do container

Ela compila durante o build da imagem, então precisa de um pom.xml funcional em projects/. Até lá fica atrás de um perfil do Compose e é ignorada por um up comum:

docker compose -f docker/docker-compose.yml --profile prod up projects-prod

Servida na porta 8084 do host, e é o único serviço que fica healthy sem você iniciar nada.


🐳 Como o Docker está montado

Um único Dockerfile multi-stage gera todas as imagens. Os stages herdam uns dos outros, então o JDK é instalado uma vez só e compartilhado:

base ──────────► full ──────────► build ──────────► production
JDK 21           + clientes de     copia o fonte     só JRE
Maven, git         banco           roda o build      sem root
curl             + rede, jq…       (interno)         executa o jar

   ▲                  ▲                                  ▲
   │                  │                                  │
class-content   playground, projects                projects-prod

Dois detalhes que vale saber antes de editar qualquer coisa:

  • 📁 O contexto de build é a raiz do projeto, não a pasta docker/. Os COPY do Dockerfile são relativos à raiz.
  • 🔁 O docker-compose.override.yml é mesclado automaticamente por ter exatamente esse nome. Ele só reserva uma GPU NVIDIA e é inofensivo sem uma. Para ignorá-lo: docker compose -f docker/docker-compose.yml up.

🔧 Solução de problemas

Sintoma Causa Solução
port is already allocated Algo na máquina já usa as portas 8082–8085 Libere a porta ou mude o lado do host no docker-compose.yml
O depurador não conecta O app subiu sem a flag de depuração Reinicie com -Dspring-boot.run.jvmArguments="$JAVA_DEBUG_OPTS"
Maven baixa tudo de novo O volume maven-cache foi removido Normal na primeira execução; ele se repopula
Front-end dá erro de CORS O CORS libera a 8081 em vez da porta do host Libere http://localhost:8083 (ou a porta correspondente)

❓ Perguntas frequentes

Por que portas diferentes no host em vez de 8081 em todos?

Dois processos não podem escutar na mesma porta do host. Com os três mapeados para 8081:8081, só um subiria por vez e os outros falhariam com port is already allocated. Separar o lado do host permite rodar o exercício da aula e a sua própria versão dele ao mesmo tempo — enquanto dentro dos containers tudo continua na 8081 canônica.

Por que um Dockerfile só, e não um por pasta?

Os três ambientes são Java 21 + Spring Boot; a diferença é só quanto ferramental cada um instala. Multi-stage resolve exatamente isso: a camada do JDK é construída uma vez e compartilhada, e não há risco de três arquivos divergirem para versões diferentes de Java. Dividir só faz sentido quando os runtimes realmente divergem.

Por que Checkstyle e JUnit não estão instalados na imagem?

Em Java isso não é possível. Não existe um apt install junit que um build fosse usar — são artefatos que o Maven resolve a partir do pom.xml. Mantê-los lá significa que adicionar uma ferramenta é uma edição de uma linha, sem reconstruir imagem nenhuma.

Por que o class-content tem porta de debug se é o ambiente "mínimo"?

Porque incluir depuração não custa nada: o JDWP é parte da JVM, não um complemento. E exercício de aula é justamente onde parar num breakpoint mais ensina.

Preciso ter Java instalado na minha máquina?

Não. O JDK, o Maven e todas as ferramentas vivem dentro dos containers. Docker é o único requisito.