Spring Boot Aufbau – Tag 7 von 10
Von Elyndra Valen, Senior Entwicklerin bei Java Fleet Systems Consulting


Messaging

🗺️ Deine Position im Kurs

TagThemaStatus
1Auto-Configuration & Custom Starter✅ Abgeschlossen
2Spring Data JPA Basics✅ Abgeschlossen
3JPA Relationships & Queries✅ Abgeschlossen
4Spring Security Part 1 – Authentication✅ Abgeschlossen
5Spring Security Part 2 – Authorization✅ Abgeschlossen
6Spring Boot Caching & JSON✅ Abgeschlossen
→ 7Messaging & Email👉 DU BIST HIER!
8Testing & Dokumentation📜 Kommt als nächstes
9Spring Boot Actuator🔒 Noch nicht freigeschaltet
10Template Engines & Microservices🔒 Noch nicht freigeschaltet

Modul: Spring Boot Aufbau
Gesamt-Dauer: 10 Arbeitstage (je 8 Stunden)
Dauer heute: 8 Stunden
Dein Ziel: Email-Versand und asynchrone Kommunikation implementieren


📋 Voraussetzungen für diesen Tag

Du brauchst:

  • ✅ Java JDK 21 installiert
  • ✅ Maven 3.8+ oder deine IDE
  • ✅ Docker (für MailDev – empfohlen!)
  • ✅ Grundlegendes Spring Boot Verständnis (Tage 1-6)

Optional (hilft beim Verständnis):

  • SMTP-Grundkenntnisse
  • Thread-Konzepte in Java

Tag verpasst oder später eingestiegen?
Kein Problem! Dieser Blogbeitrag deckt genau den Stoff von Tag 7 ab. Lade das Projekt herunter und arbeite die 8 Stunden durch!


⚡ Kurze Zusammenfassung – Das Wichtigste in 30 Sekunden

Heute lernst du:

  • ✅ Email-Versand mit Spring Boot Mail und JavaMailSender
  • ✅ HTML-Emails mit Thymeleaf Templates erstellen
  • ✅ Asynchronen Email-Versand für bessere Performance
  • ✅ JMS Messaging mit Artemis nutzen
  • ✅ Message-Driven Architecture verstehen

Das Problem: User-Registrierung dauert 5 Sekunden, weil Email synchron versendet wird.
Die Lösung: Asynchroner Email-Versand oder JMS – User bekommt sofort Response!


💡 Was du heute baust

Einen Email-Service, der Welcome-Emails versendet wenn sich User registrieren. Du lernst den Unterschied zwischen synchronem UND asynchronem Email-Versand, erstellst HTML-Templates und verstehst Message-Driven Architecture!

Dein Tagesziel: Nach 8 Stunden kannst du Emails versenden, HTML-Templates nutzen und verstehst, warum asynchrone Kommunikation wichtig ist.


📧 Warum Email & Messaging?

Hi! 👋

Elyndra hier. Letzte Woche hatte ich ein interessantes Problem: Eine User-Registrierung brauchte 5 Sekunden Response-Zeit. 5 Sekunden! Der User wartete und wartete… warum?

Weil wir die Welcome-Email synchron versendet haben. Der Server hat buchstäblich gewartet, bis die Email raus war, bevor er dem User „Registrierung erfolgreich“ zurückgegeben hat.

Das Aha-Erlebnis: Als ich den Email-Versand asynchron gemacht habe, war die Response-Zeit bei 200ms. Der User war glücklich, die Email kam trotzdem an, und der Server konnte sich um andere Requests kümmern.

Heute lernst du genau das:

  • Wie man Emails versendet (sync und async)
  • Wie man HTML-Templates nutzt
  • Wie man mit JMS Messages zwischen Services schickt
  • Warum asynchrone Kommunikation wichtig ist

Zeit, loszulegen! 🔧


🟢 GRUNDLAGEN – Deine ersten Schritte

Schritt 1: Projekt-Setup

Was du brauchst:

Öffne dein pom.xml und füge diese Dependencies hinzu:

<!-- Spring Boot Mail -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

<!-- Thymeleaf für Email-Templates -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<!-- Artemis für JMS (später) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-artemis</artifactId>
</dependency>

Was macht was?

  • spring-boot-starter-mail – Ermöglicht Email-Versand
  • spring-boot-starter-thymeleaf – Für HTML-Templates
  • spring-boot-starter-artemis – Für Message-Queues (kommt später)

✅ Checkpoint: Dependencies hinzugefügt? Mach ein mvn clean install um sie herunterzuladen!


Schritt 2: MailDev aufsetzen (WICHTIG!)

Was ist MailDev?

MailDev ist ein Test-Email-Server. Alle Emails, die du sendest, werden abgefangen und in einer Web-UI angezeigt – kein echter Email-Versand! Perfekt für Development!

Analogie: Stell dir vor, du wirfst Briefe in einen Briefkasten, aber die Briefe landen nicht bei der Post, sondern in einer Sammelbox, die du jederzeit öffnen kannst.

MailDev starten:

# Mit Docker (einfachster Weg)
docker run -p 1080:1080 -p 1025:1025 maildev/maildev

Was passiert hier?

  • Port 1025 – SMTP-Server (für Email-Versand)
  • Port 1080 – Web-UI (zum Emails ansehen)

✅ Checkpoint:

  • Öffne http://localhost:1080 – siehst du die MailDev-Oberfläche?
  • Wenn ja: Perfekt! Weiter geht’s!
  • Wenn nein: Ist Docker gestartet? Läuft MailDev? docker ps zeigt laufende Container.

Schritt 3: Spring Boot konfigurieren

application.properties bearbeiten:

Öffne src/main/resources/application.properties und füge hinzu:

# SMTP-Server (MailDev)
spring.mail.host=localhost
spring.mail.port=1025
spring.mail.from=noreply@javafleet.de

# Logging (damit du siehst was passiert)
logging.level.de.javafleet.messaging=INFO

Was bedeutet das?

  • spring.mail.host – Wo ist der SMTP-Server? (localhost = dein PC)
  • spring.mail.port – Auf welchem Port? (1025 = MailDev)
  • spring.mail.from – Von welcher Absender-Adresse?

✅ Checkpoint: Properties gespeichert? Weiter!


Schritt 4: Erste simple Email versenden

EmailService erstellen:

Erstelle eine neue Klasse EmailService.java:

package de.javafleet.messaging.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
@Slf4j
public class EmailService {
    
    private final JavaMailSender mailSender;
    
    public void sendSimpleEmail(String to, String subject, String text) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom("noreply@javafleet.de");
        message.setTo(to);
        message.setSubject(subject);
        message.setText(text);
        
        log.info("📧 Sending email to: {}", to);
        mailSender.send(message);
        log.info("✅ Email sent successfully!");
    }
}

Erklärung Zeile für Zeile:

  • @Service – Spring verwaltet diese Klasse
  • JavaMailSender – Spring injiziert automatisch den Email-Sender
  • SimpleMailMessage – Einfache Text-Email (kein HTML)
  • message.setFrom(...) – Absender-Adresse
  • message.setTo(...) – Empfänger-Adresse
  • message.setSubject(...) – Email-Betreff
  • message.setText(...) – Email-Text
  • mailSender.send(...) – JETZT wird die Email verschickt!

✅ Checkpoint: Service erstellt? Kompiliert es? (mvn compile)


Schritt 5: Controller zum Testen erstellen

MessagingController erstellen:

package de.javafleet.messaging.controller;

import de.javafleet.messaging.service.EmailService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("/api/email")
@RequiredArgsConstructor
public class MessagingController {
    
    private final EmailService emailService;
    
    @PostMapping("/send")
    public Map<String, String> sendEmail(
            @RequestParam String to,
            @RequestParam String subject,
            @RequestParam String text) {
        
        emailService.sendSimpleEmail(to, subject, text);
        
        return Map.of(
            "status", "success",
            "message", "Email sent to " + to
        );
    }
}

Was macht dieser Controller?

  • @RestController – Macht diese Klasse zu einem REST-Endpoint
  • @PostMapping("/send") – POST-Request auf /api/email/send
  • @RequestParam – Holt Parameter aus der URL
  • emailService.sendSimpleEmail(...) – Ruft unseren Service auf

✅ Checkpoint: Controller erstellt? Spring Boot starten: mvn spring-boot:run


Schritt 6: Erste Email versenden! 🎉

Teste es mit curl:

curl -X POST "http://localhost:8080/api/email/send" \
  -d "to=test@example.com" \
  -d "subject=Hello from Spring Boot" \
  -d "text=This is my first email!"

Was sollte passieren:

  1. In der Console siehst du:
📧 Sending email to: test@example.com
✅ Email sent successfully!
  1. curl zeigt:
{"status":"success","message":"Email sent to test@example.com"}
  1. In MailDev (http://localhost:1080) siehst du die Email!

✅ Checkpoint:

  • [ ] Email in MailDev angekommen?
  • [ ] Betreff und Text korrekt?
  • [ ] Funktioniert alles?

Wenn ja: GLÜCKWUNSCH! 🎉 Du hast deine erste Email mit Spring Boot versendet!

Wenn nein:

  • MailDev läuft? docker ps
  • Spring Boot läuft? Keine Fehler in der Console?
  • Port 8080 frei? Anderer Service läuft da?

Schritt 7: HTML-Email mit Template

Warum HTML?

Text-Emails sind langweilig! Mit HTML kannst du:

  • ✅ Farben und Styling nutzen
  • ✅ Buttons einfügen
  • ✅ Bilder anzeigen
  • ✅ Professioneller aussehen

Thymeleaf Template erstellen:

Erstelle src/main/resources/templates/email/welcome.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
        }
        .container {
            max-width: 600px;
            margin: 0 auto;
            padding: 20px;
        }
        .header {
            background-color: #4CAF50;
            color: white;
            padding: 20px;
            text-align: center;
        }
        .content {
            background-color: white;
            padding: 20px;
        }
        .button {
            background-color: #4CAF50;
            color: white;
            padding: 10px 20px;
            text-decoration: none;
            display: inline-block;
            border-radius: 5px;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🚀 Willkommen bei Java Fleet!</h1>
        </div>
        <div class="content">
            <h2>Hallo <span th:text="${username}">User</span>! 👋</h2>
            <p>Willkommen an Bord! Wir freuen uns, dass du dich registriert hast.</p>
            <p><strong>Email:</strong> <span th:text="${email}">email@example.com</span></p>
            <a href="#" class="button">Zum Dashboard</a>
        </div>
    </div>
</body>
</html>

Was ist hier besonders?

  • th:text="${username}" – Thymeleaf-Variable (wird durch echten Wert ersetzt)
  • ${email} – Weitere Variable
  • Inline-CSS für Styling

✅ Checkpoint: Template erstellt? Ordner templates/email/ existiert?


Schritt 8: HTML-Email versenden

EmailService erweitern:

Füge diese Methode zu EmailService.java hinzu:

@Autowired
private SpringTemplateEngine templateEngine;

@Value("${spring.mail.from}")
private String fromEmail;

public void sendHtmlEmail(String to, String subject, String username, String email) {
    try {
        // Thymeleaf Context mit Variablen
        Context context = new Context();
        context.setVariable("username", username);
        context.setVariable("email", email);
        
        // Template verarbeiten
        String htmlContent = templateEngine.process("email/welcome", context);
        
        // HTML-Email erstellen
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
        
        helper.setFrom(fromEmail);
        helper.setTo(to);
        helper.setSubject(subject);
        helper.setText(htmlContent, true); // true = HTML!
        
        log.info("📧 Sending HTML email to: {}", to);
        mailSender.send(mimeMessage);
        log.info("✅ HTML email sent!");
        
    } catch (MessagingException e) {
        log.error("❌ Failed to send HTML email", e);
    }
}

Erklärung:

  • Context – Container für Template-Variablen
  • templateEngine.process(...) – Verwandelt Template in HTML
  • MimeMessage – Komplexere Email (HTML, Attachments, etc.)
  • helper.setText(..., true)true bedeutet „Das ist HTML!“

Imports nicht vergessen:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring6.SpringTemplateEngine;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;

✅ Checkpoint: Code kompiliert? Keine Errors?


Schritt 9: HTML-Email testen

Controller erweitern:

Füge zu MessagingController.java hinzu:

@PostMapping("/send-html")
public Map<String, String> sendHtmlEmail(
        @RequestParam String to,
        @RequestParam String username) {
    
    emailService.sendHtmlEmail(
        to,
        "Willkommen bei Java Fleet!",
        username,
        to
    );
    
    return Map.of(
        "status", "success",
        "message", "HTML email sent to " + to
    );
}

Teste es:

curl -X POST "http://localhost:8080/api/email/send-html" \
  -d "to=nova@javafleet.de" \
  -d "username=Nova"

Check MailDev (http://localhost:1080):

  • Email ist schön formatiert?
  • Grüner Header sichtbar?
  • Username „Nova“ wird angezeigt?

✅ Checkpoint:

  • [ ] HTML-Email angekommen?
  • [ ] Styling funktioniert?
  • [ ] Variablen wurden ersetzt?

Wenn ja: SUPER! 🎉 Du kannst jetzt HTML-Emails versenden!


🟢 GRUNDLAGEN – Zwischencheckpoint

Kontrolliere:

  • [ ] MailDev läuft
  • [ ] Erste simple Email versendet
  • [ ] HTML-Email mit Template erstellt
  • [ ] Template-Variablen funktionieren
  • [ ] Alles kompiliert ohne Fehler

Alle ✅? Perfekt! Du verstehst die Grundlagen!
Probleme? Schau nochmal die Schritte 1-9 durch. Jeder Schritt muss funktionieren!

Pause Zeit! ☕ Nimm dir 10 Minuten Pause. Du hast es verdient!


🟡 PROFESSIONALS – Production-Ready Skills

Jetzt wird’s ernst! Du verstehst Email-Versand – Zeit für echte Projekt-Szenarien!

Best Practice #1: Asynchroner Email-Versand

Das Problem:

Aktuell wartet dein Server, bis die Email verschickt ist:

public User registerUser(UserDTO dto) {
    User user = repository.save(dto.toEntity());
    emailService.sendHtmlEmail(...);  // Server wartet hier 2-5 Sekunden!
    return user;  // User muss warten!
}

User-Perspektive: „Warum dauert die Registrierung so lange? 😤“

Die Lösung: @Async

// In EmailService.java
@Async
public void sendHtmlEmailAsync(String to, String subject, 
                               String username, String email) {
    // Gleicher Code wie vorher
    sendHtmlEmail(to, subject, username, email);
}

Main Application aktivieren:

@SpringBootApplication
@EnableAsync  // <- Das ist wichtig!
public class MessagingEmailApplication {
    public static void main(String[] args) {
        SpringApplication.run(MessagingEmailApplication.class, args);
    }
}

Jetzt verwenden:

public User registerUser(UserDTO dto) {
    User user = repository.save(dto.toEntity());
    emailService.sendHtmlEmailAsync(...);  // Returns SOFORT!
    return user;  // User wartet nicht!
}

Was passiert?

  1. User wird gespeichert
  2. Email-Methode wird in separatem Thread gestartet
  3. Methode returned SOFORT
  4. Email wird im Hintergrund verschickt

Warum funktioniert das besser?

  • ✅ User bekommt sofort Response (200-300ms statt 5 Sekunden)
  • ✅ Server kann sich um andere Requests kümmern
  • ✅ Email kommt trotzdem an

✅ Checkpoint:

  • [ ] @EnableAsync in Main-Klasse?
  • [ ] @Async auf Email-Methode?
  • [ ] Teste: Ist die Response jetzt schneller?

Best Practice #2: Error-Handling

Häufiger Fehler:

// ❌ Was wenn SMTP-Server down ist?
@Async
public void sendEmail(...) {
    mailSender.send(message);  // Kann Exception werfen!
}

Production-Ready Lösung:

@Async
public void sendHtmlEmailAsync(String to, String subject, 
                               String username, String email) {
    try {
        sendHtmlEmail(to, subject, username, email);
        log.info("✅ Async email sent to: {}", to);
    } catch (Exception e) {
        log.error("❌ Failed to send email to: {}", to, e);
        // In Production: Zu Dead Letter Queue senden
        // Oder: Retry-Mechanismus
    }
}

Warum?

  • SMTP-Server kann offline sein
  • Netzwerk-Probleme
  • Falsche Email-Adresse
  • Exception darf nicht die ganze App crashen!

✅ Checkpoint: Error-Handling eingebaut? Teste: Was passiert bei falscher SMTP-Config?


Best Practice #3: Thread Pool Konfiguration

Problem:

Standard @Async nutzt unlimited Threads. Bei 1000 Emails = 1000 Threads = Server-Crash!

Lösung: Thread Pool konfigurieren:

@Configuration
public class AsyncConfig {
    
    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);      // Minimum 2 Threads
        executor.setMaxPoolSize(5);       // Maximum 5 Threads
        executor.setQueueCapacity(100);   // 100 Tasks in Queue
        executor.setThreadNamePrefix("async-email-");
        executor.initialize();
        return executor;
    }
}

Was bedeutet das?

  • CorePoolSize – Immer 2 Threads bereit
  • MaxPoolSize – Maximal 5 Threads gleichzeitig
  • QueueCapacity – Bis zu 100 Tasks warten in Queue

Analogie: Du hast 5 Kassierer (Threads). Maximal 5 Kunden werden gleichzeitig bedient. 100 weitere Kunden warten in der Schlange (Queue).

✅ Checkpoint: Config erstellt? Spring Boot startet ohne Fehler?


Real-World-Szenario: User-Registrierung

Situation:

User registriert sich → Welcome-Email soll raus.

Complete Implementation:

1. UserService.java:

@Service
@RequiredArgsConstructor
@Slf4j
public class UserService {
    
    private final UserRepository userRepository;
    private final EmailService emailService;
    
    @Transactional
    public User registerUser(UserDTO dto) {
        // Prüfen: Email existiert schon?
        if (userRepository.existsByEmail(dto.getEmail())) {
            throw new IllegalArgumentException("Email already exists!");
        }
        
        // User speichern
        User user = userRepository.save(dto.toEntity());
        log.info("💾 User saved: {}", user.getId());
        
        // Email ASYNCHRON versenden
        emailService.sendWelcomeEmailAsync(user);
        log.info("⚡ Welcome email triggered (async)");
        
        return user;
    }
}

2. Controller:

@PostMapping("/users/register")
public ResponseEntity<Map<String, Object>> registerUser(
        @Valid @RequestBody UserDTO dto) {
    
    long start = System.currentTimeMillis();
    User user = userService.registerUser(dto);
    long duration = System.currentTimeMillis() - start;
    
    return ResponseEntity.status(HttpStatus.CREATED).body(Map.of(
        "status", "success",
        "userId", user.getId(),
        "email", user.getEmail(),
        "durationMs", duration  // Zeigt: Sehr schnell!
    ));
}

Teste es:

curl -X POST http://localhost:8080/api/users/register \
  -H "Content-Type: application/json" \
  -d '{
    "username": "testuser",
    "email": "test@example.com",
    "password": "password123"
  }'

Response:

{
  "status": "success",
  "userId": 1,
  "email": "test@example.com",
  "durationMs": 150  ← Sehr schnell!
}

Check MailDev: Welcome-Email ist da! (kommt nach 1-2 Sekunden an)

✅ Checkpoint:

  • [ ] User-Registrierung funktioniert?
  • [ ] Response-Zeit < 500ms?
  • [ ] Email kommt trotzdem an?

Häufige Probleme & Lösungen

Problem 1: @Async funktioniert nicht

Email wird synchron versendet, nicht async!

Lösung:

  1. @EnableAsync in Main-Klasse vorhanden?
  2. Methode ist public?
  3. Methode wird nicht von derselben Klasse aufgerufen?

Wichtig: @Async funktioniert nur bei Aufrufen von außen!

// ❌ Funktioniert NICHT:
public void method1() {
    this.asyncMethod();  // Same class!
}

// ✅ Funktioniert:
public void method1() {
    someOtherService.asyncMethod();  // Different class!
}

Problem 2: Email wird nicht versendet

Lösung-Checkliste:

  1. MailDev läuft? docker ps
  2. Port 1025 frei? netstat -an | grep 1025
  3. application.properties korrekt?
  4. Logs checken: Gibt es Exceptions?

Problem 3: Template nicht gefunden

TemplateNotFoundException: email/welcome

Lösung:

  1. Template liegt in src/main/resources/templates/email/welcome.html?
  2. Ordner heißt templates, nicht template?
  3. Maven: mvn clean install (repackaged Ressourcen)

🟡 PROFESSIONALS – Zwischencheckpoint

Kontrolliere:

  • [ ] @Async Email-Versand implementiert
  • [ ] Error-Handling eingebaut
  • [ ] Thread Pool konfiguriert
  • [ ] User-Registrierung mit async Email funktioniert
  • [ ] Response-Zeit deutlich schneller

Alle ✅? Du bist Production-Ready! 🚀

Pause! ☕
Nimm dir 10 Minuten. Der Bonus-Teil kommt gleich!


🔵 BONUS – Für die Neugierigen

Du hast die Basics drauf und bist Production-Ready?
Perfekt! Hier ist noch mehr, wenn du tiefer eintauchen willst.

⚠️ Optional! Überspringe diesen Teil, wenn dir der Kopf raucht.
Du kannst später zurückkommen.


Advanced Feature: JMS Messaging

Was ist JMS?

JMS = Java Message Service. Eine Queue für Messages zwischen Services.

Analogie:

  • @Async: Brief direkt übergeben (vergessen bei Neustart)
  • JMS: Brief in Briefkasten (überlebt Neustart, automatic Retry)

Wann brauchst du JMS?

  • Messages sollen persistent sein (überleben Restart)
  • Retry bei Fehlern automatisch
  • Microservices kommunizieren miteinander
  • Guaranteed Delivery wichtig

Aktivieren:

In application.properties:

# JMS mit embedded Artemis
spring.artemis.mode=embedded
spring.jms.pub-sub-domain=false

Main Application:

@SpringBootApplication
@EnableAsync
@EnableJms  // <- JMS aktivieren!
public class MessagingEmailApplication {
    // ...
}

Message Producer:

@Service
@RequiredArgsConstructor
public class UserService {
    
    private final JmsTemplate jmsTemplate;
    
    public User registerUserWithJMS(UserDTO dto) {
        User user = userRepository.save(dto.toEntity());
        
        // Message zu Queue senden
        jmsTemplate.convertAndSend("user.registered", user);
        log.info("📨 JMS: Message sent to queue");
        
        return user;
    }
}

Message Consumer:

@Component
@RequiredArgsConstructor
@Slf4j
public class UserRegistrationListener {
    
    private final EmailService emailService;
    
    @JmsListener(destination = "user.registered")
    public void handleUserRegistration(User user) {
        log.info("📬 JMS: Received message for: {}", user.getEmail());
        emailService.sendWelcomeEmail(user);
    }
}

Was passiert?

  1. User wird registriert
  2. Message wird in Queue „user.registered“ gelegt
  3. Listener nimmt Message aus Queue
  4. Email wird versendet
  5. Bei Fehler: Automatic Retry!

Vorteile:

  • ✅ Messages überleben Server-Restart
  • ✅ Automatic Retry bei Fehlern
  • ✅ Guaranteed Delivery
  • ✅ Mehrere Listener möglich

Nachteil:

  • 🐢 Langsamer als @Async
  • Braucht Message-Broker (Artemis/RabbitMQ/Kafka)

✅ Bonus-Checkpoint:

  • [ ] JMS aktiviert?
  • [ ] Message wird in Queue gelegt?
  • [ ] Listener empfängt Message?
  • [ ] Email wird versendet?

Merke dir: Du musst JMS NICHT jetzt verstehen. Komm zurück, wenn du Microservices baust!


Performance-Optimierung: Batch-Email-Versand

Problem:

1000 Emails einzeln versenden = 1000 SMTP-Connections = langsam!

Lösung: Batch-Versand

public void sendBatchEmails(List<User> users) {
    MimeMessage[] messages = new MimeMessage[users.size()];
    
    for (int i = 0; i < users.size(); i++) {
        messages[i] = createEmailFor(users.get(i));
    }
    
    // Alle auf einmal senden!
    mailSender.send(messages);
    log.info("📨 Sent {} emails in batch", users.size());
}

Performance:

  • Einzeln: 1000 Emails = 30-60 Sekunden
  • Batch: 1000 Emails = 5-10 Sekunden

Wann nutzen?

  • Newsletter-Versand
  • Massenmails
  • Reports an viele User

Tiefenwissen: Spring Events als Alternative

Was sind Spring Events?

In-Memory Events innerhalb einer Spring Boot App. Keine externe Queue.

Event erstellen:

public class UserRegisteredEvent extends ApplicationEvent {
    private final User user;
    
    public UserRegisteredEvent(Object source, User user) {
        super(source);
        this.user = user;
    }
    
    public User getUser() {
        return user;
    }
}

Event publishen:

@Service
@RequiredArgsConstructor
public class UserService {
    
    private final ApplicationEventPublisher eventPublisher;
    
    public User registerUser(UserDTO dto) {
        User user = userRepository.save(dto.toEntity());
        
        // Event publishen
        eventPublisher.publishEvent(new UserRegisteredEvent(this, user));
        
        return user;
    }
}

Event Listener:

@Component
@RequiredArgsConstructor
public class UserEventListener {
    
    private final EmailService emailService;
    
    @EventListener
    @Async
    public void handleUserRegistered(UserRegisteredEvent event) {
        emailService.sendWelcomeEmail(event.getUser());
    }
}

Vergleich:

FeatureSpring EventsJMS
Persistence❌ Nein✅ Ja
Retry❌ Manuell✅ Automatisch
Microservices❌ Nein✅ Ja
Performance⚡ Sehr schnell🐢 Langsamer
Komplexität✅ Einfach⚠️ Komplexer

Wann was?

  • Spring Events: Single App, einfache Events, Performance wichtig
  • JMS: Microservices, Persistence wichtig, Retry nötig

🔵 BONUS – Abschluss

Du hast gesehen:

  • JMS für persistent Messages
  • Batch-Email für Performance
  • Spring Events als leichtgewichtige Alternative

Wichtig: Das musst du NICHT jetzt alles können! Komm zurück zu diesem Teil, wenn du:

  • Microservices baust
  • Newsletter-Funktion implementierst
  • Event-Driven Architecture verstehen willst

Nimm mit: Es gibt mehrere Wege, Messages zu versenden. Je nach Use-Case den richtigen wählen!


📥 Downloads & Materialien

Für diesen Tag:

Weiterführend:

GitHub-Repository: 👉 tag7-spring-boot-messaging-email


✅ Checkpoint: Hast du Tag 7 geschafft?

Kontrolliere deine Erfolge:

🟢 Grundlagen (PFLICHT):

  • [ ] MailDev läuft und funktioniert
  • [ ] Erste simple Email versendet
  • [ ] HTML-Email mit Thymeleaf Template erstellt
  • [ ] Template-Variablen funktionieren
  • [ ] Email wird in MailDev angezeigt

🟡 Professionals (EMPFOHLEN):

  • [ ] @Async Email-Versand implementiert
  • [ ] Response-Zeit deutlich verbessert (< 500ms)
  • [ ] Error-Handling eingebaut
  • [ ] Thread Pool konfiguriert
  • [ ] User-Registrierung mit async Email funktioniert

🔵 Bonus (OPTIONAL):

  • [ ] JMS ausprobiert
  • [ ] Spring Events verstanden
  • [ ] Vergleich @Async vs JMS vs Events

✅ Alle Grundlagen-Häkchen gesetzt?
Glückwunsch! Du bist bereit für Tag 8! 🎉

❌ Nicht alles funktioniert?
Kein Problem! Schau nochmal in:

  • Die Grundlagen-Schritte 1-9
  • Die Troubleshooting-Sektion
  • Das finale Projekt zum Vergleichen

Brauchst du mehr Zeit?
Nimm dir die Zeit! Qualität vor Geschwindigkeit. 💪
Besser Tag 7 richtig verstanden, als halbfertig zu Tag 8!


❓ FAQ (Häufige Fragen)

Q: Ich komme bei Schritt 5 nicht weiter – Email wird nicht versendet!
A: Checke: (1) MailDev läuft? docker ps. (2) application.properties korrekt? (3) Port 1025 frei? (4) Logs in Console prüfen!

Q: Wie lange sollte ich für diesen Tag brauchen?
A: Ca. 8 Stunden mit Pausen. Grundlagen: 4h, Professionals: 3h, Bonus: 1h. Wenn du länger brauchst – kein Problem!

Q: Muss ich die Bonus-Sektion machen?
A: Nein! Bonus ist optional. Fokussiere dich auf Grundlagen & Professionals. Bonus kannst du später nachholen!

Q: @Async funktioniert nicht – was tun?
A: (1) @EnableAsync in Main-Klasse? (2) Methode ist public? (3) Methode wird von anderer Klasse aufgerufen (nicht this.asyncMethod())?

Q: Gmail statt MailDev – wie?
A: Nicht empfohlen für Development! Aber möglich: (1) Gmail App-Passwort erstellen, (2) application.properties anpassen (siehe Projekt-README), (3) SMTP-Port 587.

Q: Wann @Async und wann JMS?
A: @Async: Einfache Tasks, Single App, Performance wichtig. JMS: Microservices, Persistence wichtig, Retry nötig.

Q: Kann ich mehrere Email-Templates haben?
A: Ja! Erstelle einfach mehrere .html Dateien in /templates/email/. Z.B.: welcome.html, password-reset.html, order-confirmation.html.

Q: Was macht ihr eigentlich bei emotionalen Problemen im Team?
A: Gute Frage… Manche Geschichten passen nicht in Tech-Blogs. Die gehören eher zu… wie sagt Nova immer? Herz Schmerz und private logs. Aber das ist ein anderes Kapitel. 📖

Q: Wie teste ich Email-Versand?
A: Am besten mit MailDev! Alternativ: Spring Mail Test Support mit @MockBean(JavaMailSender.class) in Unit-Tests.


🔥 Elyndras Real Talk

Mein Email-Horror:

Vor 3 Jahren hab ich eine Newsletter-Funktion gebaut. Synchroner Versand an 1000 User. Die Queue lief voll, der Server crashed nach 5 Minuten. Franz war… not amused. 😅

Was war falsch?

  • ❌ Kein @Async
  • ❌ Kein Thread Pool Limit
  • ❌ Kein Error-Handling
  • ❌ Keine Retry-Logic

Die Lektion:

  • IMMER async bei Email-Versand
  • IMMER Thread Pool konfigurieren
  • IMMER Error-Handling
  • ✅ Bei wichtigen Emails: JMS mit Dead Letter Queue

Nova kam zu mir:

„Elyndra, warum dauert meine User-Registrierung 5 Sekunden?“

Ich schaute in ihren Code… synchroner Email-Versand. Classic Rookie-Mistake!

„Nova, schau: Der User wartet, bis die Email raus ist. Mach’s async – der User bekommt sofort Response, die Email läuft im Hintergrund.“

Sie probierte es aus: 5 Sekunden → 200ms. Ihr Gesicht war unbezahlbar! 😄

Was ich heute empfehle:

Development:

  • MailDev + @Async (einfach, schnell, gut)
  • Spring Events für interne Communication

Production:

  • Real SMTP (SendGrid, AWS SES, etc.)
  • JMS für kritische Emails (Bestellbestätigungen)
  • Dead Letter Queue für Failed Messages
  • Monitoring mit Spring Boot Actuator

Der wichtigste Tipp: Email-Versand kann fehlschlagen. Plan for failure! Error-Handling, Retry-Logic, Monitoring.

Kofi sagte letztens:

„Email ist wie Briefpost. Manchmal kommt der Brief an, manchmal nicht. Wichtig ist: Der Postbote (deine App) darf nicht hängenbleiben, wenn ein Brief nicht rausgeht.“

Besser kann man es nicht sagen! 📧


🗺️ Deine nächsten Schritte

✅ Du hast Tag 7 geschafft! Was jetzt?

Nächster Tag:

  • 📜 Tag 8: Testing & Dokumentation
    • Unit Testing mit JUnit 5 & Mockito
    • Integration Testing mit @SpringBootTest
    • API-Dokumentation mit OpenAPI/Swagger
    • Test-Driven Development (TDD)
  • 📅 Veröffentlicht: Morgen
  • ⏱️ Dauer: 8 Stunden

Vorbereitung für Tag 8:

  • [ ] Tag 7 Grundlagen vollständig ✅
  • [ ] @Async Email-Versand verstanden
  • [ ] Email-Service funktioniert
  • [ ] Projekt läuft ohne Fehler

Noch nicht bereit?
Kein Problem! Arbeite Tag 7 nochmal nach. Testing macht erst Sinn, wenn du Email-Versand verstanden hast. Qualität vor Tempo! 💪

Alle Blogbeiträge dieser Serie:
👉 Spring Boot Aufbau – Komplette Übersicht


Das war Tag 7 von Spring Boot Aufbau!

Du kannst jetzt:

  • ✅ Emails mit Spring Boot Mail versenden
  • ✅ HTML-Templates mit Thymeleaf erstellen
  • ✅ Asynchronen Email-Versand für bessere Performance
  • ✅ Den Unterschied zwischen @Async, JMS und Spring Events
  • ✅ Production-ready Email-Services bauen

Morgen lernst du Testing & Dokumentation! 🚀

Keep coding, keep learning! 💙


Tag 8 erscheint morgen. Bis dahin: Happy Coding!

P.S.: Manchmal verstecken sich die interessantesten Geschichten nicht in Code-Repositories, sondern in den… nun ja, private logs. Probier mal die Suche oben auf java-developer.online! 😉


Tags: #SpringBoot #Email #Messaging #JMS #Async #Tag7 #Kurs

Autor

  • Elyndra Valen

    28 Jahre alt, wurde kürzlich zur Senior Entwicklerin befördert nach 4 Jahren intensiver Java-Entwicklung. Elyndra kennt die wichtigsten Frameworks und Patterns, beginnt aber gerade erst, die tieferen Zusammenhänge und Architektur-Entscheidungen zu verstehen. Sie ist die Brücke zwischen Junior- und Senior-Welt im Team.