TP5.3 - التكوين حسب ملفات التعريف
الأهداف
في نهاية هذا التمرين، ستكون قادرًا على:
- ✅ إنشاء ملفات شخصية لـ dev، test، و prod
- ✅ إنشاء Beans مشروطة حسب ملف التعريف
- ✅ استخدام ملفات
application-\{profile\}.properties - ✅ تفعيل ملف شخصي واختباره
المدة المقدرة: 1 ساعة و30 دقيقة
المتطلبات الأساسية
- ✅ إكمال الفصل 4 من الوحدة 5
- ✅ إكمال TP5.1 و TP5.2
- ✅ Maven 3.9+ و Java 17+
التحضير: مشروع جديد
إنشاء المشروع
عبر Spring Initializr:
- Project: Maven
- Spring Boot: 3.2.0
- Artifact:
profiles-demo - Dependencies: Spring Web, Spring Data JPA, H2 Database, PostgreSQL Driver
الهيكل:
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
التمرين 1: ملفات شخصية لخدمات البريد الإلكتروني
الهدف
إنشاء 2 تنفيذات لـ EmailService: واحدة وهمية لـ dev، وواحدة حقيقية لـ prod.
الخطوة 1.1: إنشاء الواجهة
الملف: service/EmailService.java
package com.example.profiles.service;
public interface EmailService {
void send(String to, String subject, String body);
}
الخطوة 1.2: تنفيذ DEV (وهمي)
الملف: service/FakeEmailService.java
package com.example.profiles.service;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Service
@Profile("dev") // ← نشط فقط في 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 simulé - pas envoyé réellement)");
}
}
النقاط الرئيسية:
@Profile("dev")→ يتم إنشاء Bean فقط في ملف dev- بريد إلكتروني وهمي → لا يوجد إرسال حقيقي
الخطوة 1.3: تنفيذ PROD (حقيقي)
الملف: service/SmtpEmailService.java
package com.example.profiles.service;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Service
@Profile("prod") // ← نشط فقط في prod
public class SmtpEmailService implements EmailService {
@Override
public void send(String to, String subject, String body) {
System.out.println("========== SMTP EMAIL ==========");
System.out.println("Connexion au serveur SMTP...");
System.out.println("Authentification...");
System.out.println("Envoi à: " + to);
System.out.println("Sujet: " + subject);
System.out.println("================================");
System.out.println("(Email réellement envoyé)");
// En production, ici vous utiliseriez JavaMail:
// Session session = Session.getInstance(properties, authenticator);
// MimeMessage message = new MimeMessage(session);
// Transport.send(message);
}
}
الخطوة 1.4: إنشاء وحدة التحكم للاختبار
الملف: 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",
"Ceci est un email de test"
);
return "✅ Email envoyé via profil: " + activeProfile +
" - Voir console";
}
@GetMapping("/profile")
public String getActiveProfile() {
return "Profil actif: " + activeProfile;
}
}
الخطوة 1.5: تفعيل ملف DEV
الملف: src/main/resources/application.properties
# Profil actif
spring.profiles.active=dev
# Nom de l'application
spring.application.name=Profiles Demo
الخطوة 1.6: اختبار في DEV
ابدأ:
mvn clean install -DskipTests spring-boot:run
وحدة التحكم عند البدء:
The following 1 profile is active: "dev"
اختبر:
http://localhost:8080/api/test/email
وحدة التحكم:
========== FAKE EMAIL ==========
To: user@example.com
Subject: Test Email
Body: Ceci est un email de test
================================
(Email simulé - pas envoyé réellement)
✅ في DEV: بريد إلكتروني وهمي!
الخطوة 1.7: اختبار في PROD
عدل application.properties:
spring.profiles.active=prod
أعد التشغيل واختبر:
وحدة التحكم:
The following 1 profile is active: "prod"
========== SMTP EMAIL ==========
Connexion au serveur SMTP...
Authentification...
Envoi à: user@example.com
Sujet: Test Email
================================
(Email réellement envoyé)
✅ في PROD: بريد إلكتروني حقيقي!
التمرين 2: تكوين قاعدة البيانات حسب ملف التعريف
الهدف
استخدام H2 في dev/test، PostgreSQL في prod.
الخطوة 2.1: ملف application.properties (مشترك)
الملف: src/main/resources/application.properties
# Profil actif (changez selon besoin)
spring.profiles.active=dev
# Configuration commune
spring.application.name=Profiles Demo
server.port=8080
# JPA commun
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
الخطوة 2.2: تكوين DEV (H2)
الملف: src/main/resources/application-dev.properties
# Base de données H2 en mémoire
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
الخطوة 2.3: تكوين TEST (H2 مع سكربتات)
الملف: src/main/resources/application-test.properties
# Base de données H2 en mémoire pour tests
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 minimaux pour tests
logging.level.root=WARN
logging.level.com.example.profiles=INFO
الخطوة 2.4: تكوين PROD (PostgreSQL)
الملف: src/main/resources/application-prod.properties
# Base de données 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 minimaux
logging.level.root=WARN
logging.level.com.example.profiles=INFO
# Sécurité (en production, utilisez des variables d'environnement!)
# spring.datasource.username=${DB_USERNAME}
# spring.datasource.password=${DB_PASSWORD}
الخطوة 2.5: إنشاء كيان اختبار
الملف: 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;
// Constructeurs
public User() {}
public User(String name, String email) {
this.name = name;
this.email = email;
}
// Getters et 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; }
}
الخطوة 2.6: اختبار قواعد البيانات المختلفة
في DEV (H2):
spring.profiles.active=dev
ابدأ وتحقق من وحدة التحكم:
H2 console available at '/h2-console'
Hibernate: create table users (...)
Database: H2
الوصول إلى وحدة تحكم H2:
http://localhost:8080/h2-console
JDBC URL: jdbc:h2:mem:devdb
Username: sa
Password: (vide)
في PROD (PostgreSQL):
- أنشئ قاعدة البيانات أولاً:
CREATE DATABASE proddb;
- عدل ملف التعريف:
spring.profiles.active=prod
- ابدأ:
Database: PostgreSQL
Hibernate: validate schema
التمرين 3: Beans التكوين حسب ملف التعريف
الهدف
إنشاء Beans مختلفة حسب البيئة.
الخطوة 3.1: تكوين DEV
الملف: 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") // ← Toute la classe active 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";
}
}
الخطوة 3.2: تكوين PROD
الملف: 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") // ← Toute la classe active 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";
}
}
الخطوة 3.3: اختبار
أضف في 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;
}
// ... autres méthodes ...
@GetMapping("/config")
public String getConfig() {
return String.format(
"Environnement: %s<br>" +
"Profil actif: %s<br>" +
"Mode debug: %s<br>" +
"API URL: %s",
environmentName,
activeProfile,
debugMode,
apiUrl
);
}
}
في DEV:
Environnement: DEVELOPMENT
Profil actif: dev
Mode debug: true
API URL: http://localhost:8080/api
في PROD:
Environnement: PRODUCTION
Profil actif: prod
Mode debug: false
API URL: https://api.monsite.com/api
التمرين 4: ملفات شخصية متعددة و @Profile مركب
الهدف
تفعيل ملفات شخصية متعددة وإنشاء شروط معقدة.
الخطوة 4.1: Bean لملفات شخصية متعددة
أضف في config/DevConfig.java:
@Bean
@Profile({"dev", "test"}) // ← Actif en dev OU test
public String sharedDevTestBean() {
return "Bean partagé entre dev et test";
}
الخطوة 4.2: Bean شرطية معقدة
@Bean
@Profile("!prod") // ← Actif dans TOUS les profils SAUF prod
public String nonProdBean() {
return "Bean pour non-production (dev, test, etc.)";
}
الخطوة 4.3: تفعيل ملفات شخصية متعددة
الطريقة 1: application.properties
spring.profiles.active=dev,mock-data,debug
الطريقة 2: سطر الأوامر
mvn spring-boot:run -Dspring-boot.run.profiles=dev,mock-data
الطريقة 3: متغير البيئة
export SPRING_PROFILES_ACTIVE=prod,monitoring
mvn spring-boot:run
التمرين 5: تكوين كامل متعدد البيئات
الهدف
إنشاء تكوين جاهز للإنتاج.
الخطوة 5.1: متغيرات البيئة في PROD
الملف: application-prod.properties
# Base de données (depuis variables d'environnement)
spring.datasource.url=${DATABASE_URL:jdbc:postgresql://localhost:5432/proddb}
spring.datasource.username=${DB_USERNAME:admin}
spring.datasource.password=${DB_PASSWORD}
# SMTP (depuis variables d'environnement)
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}
الصيغة:
$\{VAR_NAME\}→ متغير إلزامي (خطأ إذا كان غائبًا)${VAR_NAME:default}→ متغير بقيمة افتراضية
الخطوة 5.2: النشر مع المتغيرات
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
الملخص
هيكل الملفات
application.properties ← تكوين مشترك
application-dev.properties ← خاص بـ dev
application-test.properties ← خاص بـ test
application-prod.properties ← خاص بـ prod
ترتيب الأولوية:
1. application-{profile}.properties (خاص)
2. application.properties (مشترك)
3. متغيرات البيئة
4. معلمات سطر الأوامر
Beans حسب ملف التعريف
// Bean لملف شخصي واحد
@Profile("dev")
// Bean لعدة ملفات شخصية
@Profile({"dev", "test"})
// Bean لكل الملفات الشخصية ما عدا واحد
@Profile("!prod")
// الفئة بأكملها
@Configuration
@Profile("dev")
public class DevConfig { }
تفعيل ملف شخصي
1. application.properties:
spring.profiles.active=dev
2. سطر الأوامر:
--spring.profiles.active=prod
3. متغير البيئة:
export SPRING_PROFILES_ACTIVE=prod
4. IDE (VS Code):
"spring.profiles.active": "dev"
التقييم الذاتي
- ما الفرق بين application.properties و application-dev.properties؟
- كيف تنشئ Bean نشط فقط في dev؟
- كيف تفعل ملفات شخصية متعددة في نفس الوقت؟
- لماذا تستخدم متغيرات البيئة في الإنتاج؟
- ما هي الصيغة لمتغير بقيمة افتراضية؟
تحدي إضافي 🎯
أنشئ تكوينًا كاملاً لتطبيق حقيقي:
-
3 ملفات شخصية: dev، test، prod
-
Dev:
- H2 في الذاكرة
- بريد إلكتروني وهمي
- سجلات DEBUG
- لا أمان
-
Test:
- H2 مع سكربتات
- بريد إلكتروني وهمي
- سجلات INFO
-
Prod:
- PostgreSQL
- بريد إلكتروني SMTP حقيقي
- سجلات WARN
- الأمان مفعل
- متغيرات البيئة
إضافة: أضف ملف شخصي docker لـ Docker Compose
تهانينا! 🎉
لقد أكملت الوحدة 5!
أنت الآن تتقن:
- ✅ عكس التحكم (IoC)
- ✅ حقن التبعيات (DI)
- ✅ Beans ودورة حياتها
- ✅ النطاقات (Singleton، Prototype)
- ✅ التكوين حسب ملفات التعريف
- ✅ البيئات المتعددة (dev/test/prod)
أنت جاهز للمفاهيم المتقدمة!