TP5.3 - Configuración por Perfiles
Objetivos
Al final de este TP, usted será capaz de:
- ✅ Crear perfiles para dev, test y prod
- ✅ Crear Beans condicionales según el perfil
- ✅ Utilizar archivos
application-\{profile\}.properties - ✅ Activar un perfil y probar
Duración estimada: 1h30
Prerrequisitos
- ✅ Capítulo 4 del Módulo 5 completado
- ✅ TP5.1 y TP5.2 completados
- ✅ Maven 3.9+ y Java 17+
Preparación: Nuevo Proyecto
Crear el Proyecto
Vía Spring Initializr:
- Project: Maven
- Spring Boot: 3.2.0
- Artifact:
profiles-demo - Dependencies: Spring Web, Spring Data JPA, H2 Database, PostgreSQL Driver
Estructura:
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
Ejercicio 1: Perfiles para Servicios de Email
Objetivo
Crear 2 implementaciones de EmailService: una falsa para dev, una real para prod.
Paso 1.1: Crear la Interfaz
Archivo: service/EmailService.java
package com.example.profiles.service;
public interface EmailService {
void send(String to, String subject, String body);
}
Paso 1.2: Implementación DEV (Fake)
Archivo: service/FakeEmailService.java
package com.example.profiles.service;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Service
@Profile("dev") // ← Activo SOLO en 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 - no enviado realmente)");
}
}
Puntos clave:
@Profile("dev")→ Bean creado solo en perfil dev- Email simulado → No se envía realmente
Paso 1.3: Implementación PROD (Real)
Archivo: service/SmtpEmailService.java
package com.example.profiles.service;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Service
@Profile("prod") // ← Activo SOLO en prod
public class SmtpEmailService implements EmailService {
@Override
public void send(String to, String subject, String body) {
System.out.println("========== SMTP EMAIL ==========");
System.out.println("Conexión al servidor SMTP...");
System.out.println("Autenticación...");
System.out.println("Enviando a: " + to);
System.out.println("Asunto: " + subject);
System.out.println("================================");
System.out.println("(Email enviado realmente)");
// En producción, aquí usaría JavaMail:
// Session session = Session.getInstance(properties, authenticator);
// MimeMessage message = new MimeMessage(session);
// Transport.send(message);
}
}
Paso 1.4: Crear el Controller de Prueba
Archivo: 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 es un email de prueba"
);
return "✅ Email enviado vía perfil: " + activeProfile +
" - Ver consola";
}
@GetMapping("/profile")
public String getActiveProfile() {
return "Perfil activo: " + activeProfile;
}
}
Paso 1.5: Activar el Perfil DEV
Archivo: src/main/resources/application.properties
# Perfil activo
spring.profiles.active=dev
# Nombre de la aplicación
spring.application.name=Profiles Demo
Paso 1.6: Probar en DEV
Inicie:
mvn clean install -DskipTests spring-boot:run
Consola al iniciar:
The following 1 profile is active: "dev"
Pruebe:
http://localhost:8080/api/test/email
Consola:
========== FAKE EMAIL ==========
To: user@example.com
Subject: Test Email
Body: Este es un email de prueba
================================
(Email simulado - no enviado realmente)
✅ En DEV: ¡Email simulado!
Paso 1.7: Probar en PROD
Modifique application.properties:
spring.profiles.active=prod
Reinicie y pruebe:
Consola:
The following 1 profile is active: "prod"
========== SMTP EMAIL ==========
Conexión al servidor SMTP...
Autenticación...
Enviando a: user@example.com
Asunto: Test Email
================================
(Email enviado realmente)
✅ En PROD: ¡Email real!
Ejercicio 2: Configuración de Base de Datos por Perfil
Objetivo
Utilizar H2 en dev/test, PostgreSQL en prod.
Paso 2.1: Archivo application.properties (Común)
Archivo: src/main/resources/application.properties
# Perfil activo (cambie según necesidad)
spring.profiles.active=dev
# Configuración común
spring.application.name=Profiles Demo
server.port=8080
# JPA común
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
Paso 2.2: Configuración DEV (H2)
Archivo: src/main/resources/application-dev.properties
# Base de datos H2 en memoria
spring.datasource.url=jdbc:h2:mem:devdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# Consola 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
Paso 2.3: Configuración TEST (H2 con scripts)
Archivo: src/main/resources/application-test.properties
# Base de datos H2 en memoria para pruebas
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 pruebas
logging.level.root=WARN
logging.level.com.example.profiles=INFO
Paso 2.4: Configuración PROD (PostgreSQL)
Archivo: src/main/resources/application-prod.properties
# Base de datos 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
# Seguridad (en producción, use variables de entorno!)
# spring.datasource.username=${DB_USERNAME}
# spring.datasource.password=${DB_PASSWORD}
Paso 2.5: Crear una Entidad de Prueba
Archivo: 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;
// Constructores
public User() {}
public User(String name, String email) {
this.name = name;
this.email = email;
}
// Getters y 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; }
}
Paso 2.6: Probar las Diferentes BDs
En DEV (H2):
spring.profiles.active=dev
Inicie y verifique la consola:
H2 console available at '/h2-console'
Hibernate: create table users (...)
Database: H2
Acceda a la consola H2:
http://localhost:8080/h2-console
JDBC URL: jdbc:h2:mem:devdb
Username: sa
Password: (vacío)
En PROD (PostgreSQL):
- Cree primero la base:
CREATE DATABASE proddb;
- Modifique el perfil:
spring.profiles.active=prod
- Inicie:
Database: PostgreSQL
Hibernate: validate schema
Ejercicio 3: Beans de Configuración por Perfil
Objetivo
Crear Beans diferentes según el entorno.
Paso 3.1: Configuración DEV
Archivo: 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 la clase activa en 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";
}
}
Paso 3.2: Configuración PROD
Archivo: 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 la clase activa en 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";
}
}
Paso 3.3: Probar
Agregue en 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;
}
// ... otros métodos ...
@GetMapping("/config")
public String getConfig() {
return String.format(
"Entorno: %s<br>" +
"Perfil activo: %s<br>" +
"Modo debug: %s<br>" +
"API URL: %s",
environmentName,
activeProfile,
debugMode,
apiUrl
);
}
}
En DEV:
Entorno: DEVELOPMENT
Perfil activo: dev
Modo debug: true
API URL: http://localhost:8080/api
En PROD:
Entorno: PRODUCTION
Perfil activo: prod
Modo debug: false
API URL: https://api.monsite.com/api
Ejercicio 4: Perfiles Múltiples y @Profile Compuesto
Objetivo
Activar múltiples perfiles y crear condiciones complejas.
Paso 4.1: Bean para Varios Perfiles
Agregue en config/DevConfig.java:
@Bean
@Profile({"dev", "test"}) // ← Activo en dev O test
public String sharedDevTestBean() {
return "Bean compartido entre dev y test";
}
Paso 4.2: Bean Condicional Complejo
@Bean
@Profile("!prod") // ← Activo en TODOS los perfiles EXCEPTO prod
public String nonProdBean() {
return "Bean para no-producción (dev, test, etc.)";
}
Paso 4.3: Activar Múltiples Perfiles
Método 1: application.properties
spring.profiles.active=dev,mock-data,debug
Método 2: Línea de comandos
mvn spring-boot:run -Dspring-boot.run.profiles=dev,mock-data
Método 3: Variable de entorno
export SPRING_PROFILES_ACTIVE=prod,monitoring
mvn spring-boot:run
Ejercicio 5: Configuración Completa Multi-Entornos
Objetivo
Crear una configuración lista para producción.
Paso 5.1: Variables de Entorno en PROD
Archivo: application-prod.properties
# Base de datos (desde variables de entorno)
spring.datasource.url=${DATABASE_URL:jdbc:postgresql://localhost:5432/proddb}
spring.datasource.username=${DB_USERNAME:admin}
spring.datasource.password=${DB_PASSWORD}
# SMTP (desde variables de entorno)
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}
Sintaxis:
$\{VAR_NAME\}→ Variable obligatoria (error si falta)${VAR_NAME:default}→ Variable con valor por defecto
Paso 5.2: Desplegar con Variables
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
Resumen
Estructura de Archivos
application.properties ← Configuración común
application-dev.properties ← Específico dev
application-test.properties ← Específico test
application-prod.properties ← Específico prod
Orden de prioridad:
1. application-{profile}.properties (específico)
2. application.properties (común)
3. Variables de entorno
4. Argumentos de línea de comandos
Beans por Perfil
// Bean para UN perfil
@Profile("dev")
// Bean para VARIOS perfiles
@Profile({"dev", "test"})
// Bean para TODOS excepto uno
@Profile("!prod")
// Clase entera
@Configuration
@Profile("dev")
public class DevConfig { }
Activar un Perfil
1. application.properties:
spring.profiles.active=dev
2. Línea de comandos:
--spring.profiles.active=prod
3. Variable de entorno:
export SPRING_PROFILES_ACTIVE=prod
4. IDE (VS Code):
"spring.profiles.active": "dev"
Autoevaluación
- ¿Cuál es la diferencia entre application.properties y application-dev.properties?
- ¿Cómo crear un Bean activo solo en dev?
- ¿Cómo activar varios perfiles simultáneamente?
- ¿Por qué usar variables de entorno en producción?
- ¿Cuál es la sintaxis para una variable con valor por defecto?
Challenge Bonus 🎯
Cree una configuración completa para una aplicación real:
-
3 perfiles: dev, test, prod
-
Dev:
- H2 en memoria
- Email fake
- Logs DEBUG
- Sin seguridad
-
Test:
- H2 con scripts
- Email mock
- Logs INFO
-
Prod:
- PostgreSQL
- Email SMTP real
- Logs WARN
- Seguridad activada
- Variables de entorno
Bonus: Agregue un perfil docker para Docker Compose
¡Felicitaciones! 🎉
¡Ha completado el Módulo 5!
Ahora domina:
- ✅ Inversión de Control (IoC)
- ✅ Inyección de Dependencias (DI)
- ✅ Beans y su ciclo de vida
- ✅ Ámbitos (Singleton, Prototype)
- ✅ Configuración por perfiles
- ✅ Multi-entornos (dev/test/prod)
¡Está listo para conceptos avanzados!