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.
English 🇺🇸 · Português
- Por que três ambientes
- Estrutura do repositório
- Portas
- Requisitos
- Começando
- Comandos do dia a dia
- Depuração
- Testes e qualidade de código
- Imagem de produção
- Como o Docker está montado
- Solução de problemas
- Perguntas frequentes
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.
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.
| 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 containerEssas são as portas da sua máquina. Dentro de todos os containers o app sempre escuta na
8081e o depurador na5005. Nada no código Java ou noapplication.propertiesmenciona 8082/8083/8085 — o remapeamento existe só para os três poderem rodar ao mesmo tempo, e está definido apenas nodocker-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
fetche o CORS da API precisam usar a porta do host, não a 8081.
- 🐳 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.
- Abra o repositório no VS Code
- Execute Dev Containers: Reopen in Container na paleta de comandos (F1)
- Escolha o ambiente em que quer trabalhar
- No terminal do container, suba o app:
mvn spring-boot:runAs extensões de Java e o cache do Maven já vêm configurados.
# 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.
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 |
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:
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 |
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 + coberturaA 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-prodServida na porta 8084 do host, e é o único serviço que fica healthy sem
você iniciar nada.
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/. OsCOPYdo 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.
| 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) |
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.