Pular para o conteúdo principal

TP5.3 - Configuração por Perfis

Objetivos​

Ao final deste exercício, você será capaz de:

  1. ✅ Criar perfis para dev, test e prod
  2. ✅ Criar Beans condicionais de acordo com o perfil
  3. ✅ Utilizar arquivos application-\{profile\}.properties
  4. ✅ Ativar um perfil e testar

Duração estimada: 1h30


Pré-requisitos​

  • ✅ Capítulo 4 do Módulo 5 concluído
  • ✅ TP5.1 e TP5.2 concluídos
  • ✅ Maven 3.9+ e Java 17+

Preparação: Novo Projeto​

Criar o Projeto​

Via Spring Initializr:

  • Project: Maven
  • Spring Boot: 3.2.0
  • Artifact: profiles-demo
  • Dependencies: Spring Web, Spring Data JPA, H2 Database, PostgreSQL Driver

Estrutura:

src/main/java/com/example/profiles/
├── ProfilesDemoApplication.java
├── service/
│ ├── EmailService.java
│ ├── FakeEmailService.java
│ └── SmtpEmailService.java
├── config/
│ ├── DevConfig.java
│ ├── ProdConfig.java
│ └── DatabaseConfig.java
└── controller/
└── TestController.java

src/main/resources/
├── application.properties
├── application-dev.properties
├── application-test.properties
└── application-prod.properties

Exercício 1: Perfis para Serviços de Email​

Objetivo​

Criar 2 implementações de EmailService: uma fake para dev, uma real para prod.

Etapa 1.1: Criar a Interface​

Arquivo: service/EmailService.java

package com.example.profiles.service;

public interface EmailService {
void send(String to, String subject, String body);
}

Etapa 1.2: Implementação DEV (Fake)​

Arquivo: service/FakeEmailService.java

package com.example.profiles.service;

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

@Service
@Profile("dev") // ← Ativo SOMENTE em dev
public class FakeEmailService implements EmailService {

@Override
public void send(String to, String subject, String body) {
System.out.println("========== FAKE EMAIL ==========");
System.out.println("To: " + to);
System.out.println("Subject: " + subject);
System.out.println("Body: " + body);
System.out.println("================================");
System.out.println("(Email simulado - não enviado de verdade)");
}
}

Pontos-chave:

  • @Profile("dev") → Bean criado apenas no perfil dev
  • Email simulado → Não envia de verdade

Etapa 1.3: Implementação PROD (Real)​

Arquivo: service/SmtpEmailService.java

package com.example.profiles.service;

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

@Service
@Profile("prod") // ← Ativo SOMENTE em prod
public class SmtpEmailService implements EmailService {

@Override
public void send(String to, String subject, String body) {
System.out.println("========== SMTP EMAIL ==========");
System.out.println("Conectando ao servidor SMTP...");
System.out.println("Autenticando...");
System.out.println("Enviando para: " + to);
System.out.println("Assunto: " + subject);
System.out.println("================================");
System.out.println("(Email enviado de verdade)");

// Em produção, aqui você usaria JavaMail:
// Session session = Session.getInstance(properties, authenticator);
// MimeMessage message = new MimeMessage(session);
// Transport.send(message);
}
}

Etapa 1.4: Criar o Controller de Teste​

Arquivo: controller/TestController.java

package com.example.profiles.controller;

import com.example.profiles.service.EmailService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/test")
public class TestController {

private final EmailService emailService;

@Value("${spring.profiles.active:default}")
private String activeProfile;

public TestController(EmailService emailService) {
this.emailService = emailService;
}

@GetMapping("/email")
public String testEmail() {
emailService.send(
"user@example.com",
"Test Email",
"Este é um email de teste"
);

return "✅ Email enviado via perfil: " + activeProfile +
" - Veja o console";
}

@GetMapping("/profile")
public String getActiveProfile() {
return "Perfil ativo: " + activeProfile;
}
}

Etapa 1.5: Ativar o Perfil DEV​

Arquivo: src/main/resources/application.properties

# Perfil ativo
spring.profiles.active=dev

# Nome da aplicação
spring.application.name=Profiles Demo

Etapa 1.6: Testar em DEV​

Inicie:

mvn clean install -DskipTests spring-boot:run

Console ao iniciar:

The following 1 profile is active: "dev"

Teste:

http://localhost:8080/api/test/email

Console:

========== FAKE EMAIL ==========
To: user@example.com
Subject: Test Email
Body: Este é um email de teste
================================
(Email simulado - não enviado de verdade)

✅ Em DEV: Email simulado!


Etapa 1.7: Testar em PROD​

Altere application.properties:

spring.profiles.active=prod

Reinicie e teste:

Console:

The following 1 profile is active: "prod"

========== SMTP EMAIL ==========
Conectando ao servidor SMTP...
Autenticando...
Enviando para: user@example.com
Assunto: Test Email
================================
(Email enviado de verdade)

✅ Em PROD: Email real!


Exercício 2: Configuração de Banco de Dados por Perfil​

Objetivo​

Usar H2 em dev/test, PostgreSQL em prod.

Etapa 2.1: Arquivo application.properties (Comum)​

Arquivo: src/main/resources/application.properties

# Perfil ativo (altere conforme necessário)
spring.profiles.active=dev

# Configuração comum
spring.application.name=Profiles Demo
server.port=8080

# JPA comum
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

Etapa 2.2: Configuração DEV (H2)​

Arquivo: src/main/resources/application-dev.properties

# Banco de dados H2 em memória
spring.datasource.url=jdbc:h2:mem:devdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# Console H2
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

# Hibernate
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

# Logs
logging.level.root=DEBUG
logging.level.com.example.profiles=DEBUG

Etapa 2.3: Configuração TEST (H2 com scripts)​

Arquivo: src/main/resources/application-test.properties

# Banco de dados H2 em memória para testes
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# Hibernate
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

# Logs mínimos para testes
logging.level.root=WARN
logging.level.com.example.profiles=INFO

Etapa 2.4: Configuração PROD (PostgreSQL)​

Arquivo: src/main/resources/application-prod.properties

# Banco de dados PostgreSQL
spring.datasource.url=jdbc:postgresql://localhost:5432/proddb
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.username=admin
spring.datasource.password=admin123

# Hibernate
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect

# Logs mínimos
logging.level.root=WARN
logging.level.com.example.profiles=INFO

# Segurança (em produção, use variáveis de ambiente!)
# spring.datasource.username=${DB_USERNAME}
# spring.datasource.password=${DB_PASSWORD}

Etapa 2.5: Criar uma Entidade de Teste​

Arquivo: model/User.java

package com.example.profiles.model;

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private String email;

// Construtores
public User() {}

public User(String name, String email) {
this.name = name;
this.email = email;
}

// Getters e Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }

public String getName() { return name; }
public void setName(String name) { this.name = name; }

public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}

Etapa 2.6: Testar os Diferentes BDs​

Em DEV (H2):

spring.profiles.active=dev

Inicie e verifique o console:

H2 console available at '/h2-console'
Hibernate: create table users (...)
Database: H2

Acesse o console H2:

http://localhost:8080/h2-console
JDBC URL: jdbc:h2:mem:devdb
Username: sa
Password: (vazio)

Em PROD (PostgreSQL):

  1. Crie primeiro o banco:
CREATE DATABASE proddb;
  1. Altere o perfil:
spring.profiles.active=prod
  1. Inicie:
Database: PostgreSQL
Hibernate: validate schema

Exercício 3: Beans de Configuração por Perfil​

Objetivo​

Criar Beans diferentes de acordo com o ambiente.

Etapa 3.1: Configuração DEV​

Arquivo: config/DevConfig.java

package com.example.profiles.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
@Profile("dev") // ← Toda a classe ativa em dev
public class DevConfig {

@Bean
public String environmentName() {
return "DEVELOPMENT";
}

@Bean
public boolean debugMode() {
return true;
}

@Bean
public String apiUrl() {
return "http://localhost:8080/api";
}
}

Etapa 3.2: Configuração PROD​

Arquivo: config/ProdConfig.java

package com.example.profiles.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
@Profile("prod") // ← Toda a classe ativa em prod
public class ProdConfig {

@Bean
public String environmentName() {
return "PRODUCTION";
}

@Bean
public boolean debugMode() {
return false;
}

@Bean
public String apiUrl() {
return "https://api.monsite.com/api";
}
}

Etapa 3.3: Testar​

Adicione em TestController.java:

@RestController
@RequestMapping("/api/test")
public class TestController {

private final EmailService emailService;
private final String environmentName;
private final boolean debugMode;
private final String apiUrl;

@Value("${spring.profiles.active:default}")
private String activeProfile;

public TestController(
EmailService emailService,
String environmentName,
boolean debugMode,
String apiUrl
) {
this.emailService = emailService;
this.environmentName = environmentName;
this.debugMode = debugMode;
this.apiUrl = apiUrl;
}

// ... outros métodos ...

@GetMapping("/config")
public String getConfig() {
return String.format(
"Ambiente: %s<br>" +
"Perfil ativo: %s<br>" +
"Modo debug: %s<br>" +
"API URL: %s",
environmentName,
activeProfile,
debugMode,
apiUrl
);
}
}

Em DEV:

Ambiente: DEVELOPMENT
Perfil ativo: dev
Modo debug: true
API URL: http://localhost:8080/api

Em PROD:

Ambiente: PRODUCTION
Perfil ativo: prod
Modo debug: false
API URL: https://api.monsite.com/api

Exercício 4: Perfis Múltiplos e @Profile Composto​

Objetivo​

Ativar múltiplos perfis e criar condições complexas.

Etapa 4.1: Bean para Múltiplos Perfis​

Adicione em config/DevConfig.java:

@Bean
@Profile({"dev", "test"}) // ← Ativo em dev OU test
public String sharedDevTestBean() {
return "Bean compartilhado entre dev e test";
}

Etapa 4.2: Bean Condicional Complexo​

@Bean
@Profile("!prod") // ← Ativo em TODOS os perfis EXCETO prod
public String nonProdBean() {
return "Bean para não-produção (dev, test, etc.)";
}

Etapa 4.3: Ativar Múltiplos Perfis​

Método 1: application.properties

spring.profiles.active=dev,mock-data,debug

Método 2: Linha de comando

mvn spring-boot:run -Dspring-boot.run.profiles=dev,mock-data

Método 3: Variável de ambiente

export SPRING_PROFILES_ACTIVE=prod,monitoring
mvn spring-boot:run

Exercício 5: Configuração Completa Multi-Ambientes​

Objetivo​

Criar uma configuração pronta para produção.

Etapa 5.1: Variáveis de Ambiente em PROD​

Arquivo: application-prod.properties

# Banco de dados (a partir de variáveis de ambiente)
spring.datasource.url=${DATABASE_URL:jdbc:postgresql://localhost:5432/proddb}
spring.datasource.username=${DB_USERNAME:admin}
spring.datasource.password=${DB_PASSWORD}

# SMTP (a partir de variáveis de ambiente)
spring.mail.host=${SMTP_HOST:smtp.gmail.com}
spring.mail.port=${SMTP_PORT:587}
spring.mail.username=${SMTP_USERNAME}
spring.mail.password=${SMTP_PASSWORD}

# API Keys
app.external-api.key=${EXTERNAL_API_KEY}
app.external-api.url=${EXTERNAL_API_URL:https://api.service.com}

Sintaxe:

  • $\{VAR_NAME\} → Variável obrigatória (erro se ausente)
  • ${VAR_NAME:default} → Variável com valor padrão

Etapa 5.2: Deploy com Variáveis​

Linux/Mac:

export DATABASE_URL="jdbc:postgresql://prod-server:5432/myapp"
export DB_USERNAME="app_user"
export DB_PASSWORD="secret123"
export SMTP_USERNAME="noreply@myapp.com"
export SMTP_PASSWORD="smtp_secret"

mvn spring-boot:run

Windows:

$env:DATABASE_URL="jdbc:postgresql://prod-server:5432/myapp"
$env:DB_USERNAME="app_user"
# ...

mvn spring-boot:run

Recapitulação​

Estrutura dos Arquivos​

application.properties          ← Configuração comum
application-dev.properties ← Específico dev
application-test.properties ← Específico test
application-prod.properties ← Específico prod

Ordem de prioridade:

1. application-{profile}.properties (específico)
2. application.properties (comum)
3. Variáveis de ambiente
4. Argumentos de linha de comando

Beans por Perfil​

// Bean para UM perfil
@Profile("dev")

// Bean para MAIS DE UM perfil
@Profile({"dev", "test"})

// Bean para TODOS exceto um
@Profile("!prod")

// Classe inteira
@Configuration
@Profile("dev")
public class DevConfig { }

Ativar um Perfil​

1. application.properties:

spring.profiles.active=dev

2. Linha de comando:

--spring.profiles.active=prod

3. Variável de ambiente:

export SPRING_PROFILES_ACTIVE=prod

4. IDE (VS Code):

"spring.profiles.active": "dev"

Autoavaliação​

  1. Qual a diferença entre application.properties e application-dev.properties?
  2. Como criar um Bean ativo apenas em dev?
  3. Como ativar múltiplos perfis simultaneamente?
  4. Por que usar variáveis de ambiente em produção?
  5. Qual a sintaxe para uma variável com valor padrão?

Desafio Bônus 🎯​

Crie uma configuração completa para uma aplicação real:

  1. 3 perfis: dev, test, prod

  2. Dev:

    • H2 em memória
    • Email fake
    • Logs DEBUG
    • Sem segurança
  3. Test:

    • H2 com scripts
    • Email mock
    • Logs INFO
  4. Prod:

    • PostgreSQL
    • Email SMTP real
    • Logs WARN
    • Segurança ativada
    • Variáveis de ambiente

Bônus: Adicione um perfil docker para Docker Compose


Parabéns! 🎉​

Você concluiu o Módulo 5!

Agora você domina:

  • ✅ Inversão de Controle (IoC)
  • ✅ Injeção de Dependências (DI)
  • ✅ Beans e seu ciclo de vida
  • ✅ Escopos (Singleton, Prototype)
  • ✅ Configuração por perfis
  • ✅ Multi-ambientes (dev/test/prod)

Você está pronto para conceitos avançados!


⬅️ TP anterior: Beans e Escopos

⬅️ Voltar ao Módulo 5

➡️ Próximo módulo: Spring Data JPA Avançado