🎓 KURSABSCHLUSS – Spring Boot Aufbau (Tag 10 von 10)
Von Elyndra Valen, Lead Developer bei Java Fleet


Template Engines

📍 Deine Position im Kurs

TagThemaStatus
1Auto-Configuration & Custom StarterVoraussetzung
2Spring Data JPA BasicsVoraussetzung
3JPA Relationships & QueriesVoraussetzung
4Spring Security Part 1 – AuthenticationVoraussetzung
5Spring Security Part 2 – AuthorizationVoraussetzung
6Spring Boot Caching & JSONVoraussetzung
7Messaging & EmailVoraussetzung
8Testing & DokumentationVoraussetzung
9Spring Boot ActuatorVoraussetzung
10Template Engines & Microservices👉 DU BIST HIER!

Modul: Spring Boot Aufbau (10 Arbeitstage)
Dein Ziel: Alle Template Engines vergleichen & Microservices-Grundlagen verstehen


📋 Voraussetzungen für Tag 10

Du brauchst:

  • ✅ Abgeschlossenen Tag 9 (Actuator & Monitoring)
  • ✅ Verständnis von REST APIs (Tag 1-4)
  • ✅ Thymeleaf-Kenntnisse aus dem Basiskurs
  • ✅ Java 21 und Maven installiert

Optional (hilft beim Verständnis):

  • Docker & Docker Compose installiert (für Microservices-Teil)
  • Erfahrung mit anderen Template-Sprachen (PHP, Ruby, etc.)

⚡ Was du heute lernst:

Heute schließt du den Spring Boot Aufbau-Kurs ab! Im Vormittag vergleichst du alle wichtigen Template Engines für Spring Boot – nicht nur Thymeleaf, sondern auch Mustache, Groovy, JTE, Pebble und mehr. Im Nachmittag lernst du Microservices mit Spring Cloud.


🎯 Dein Ziel :

Template Engines:

  • ✅ Alle Spring Boot Template Engines kennen
  • ✅ Vor- und Nachteile jeder Engine verstehen
  • ✅ Wissen, wann welche Engine die richtige Wahl ist
  • ✅ Praktische Beispiele für jede Engine gesehen haben
  • ✅ Performance-Unterschiede kennen

Microservices:

  • ✅ Microservices-Architektur verstehen
  • ✅ Service Discovery mit Eureka nutzen
  • ✅ REST-Calls zwischen Services mit Feign machen
  • ✅ API Gateway konfigurieren

Hier kannst du auf GitHub die Vergleichsprojekte für die Templates herunterladen!

Hier kannst du auf GitHub die Vergleichsprodukte für die Frontend Frameworks Vue.js, React, Angular herunterladen.

Hier kannst du das Projekt für die MicroService Grundlagen mit Eureka Server und API Gateway mit Feign Client herunterladen.


🎨 Teil 1: Template Engines im Vergleich

Du kannst dir hier die Beispielprojekte herunterladen.

Warum Template Engines vergleichen?

Im Basiskurs hast du Thymeleaf kennengelernt – die Standard-Template-Engine für Spring Boot. Aber Thymeleaf ist nicht die einzige Option! Je nach Projekt-Anforderungen können andere Template Engines besser geeignet sein:

  • Mustache: Wenn du logic-less templates willst
  • Groovy Templates: Wenn dein Team Groovy kennt
  • JTE: Wenn Performance kritisch ist
  • Pebble: Wenn du aus der PHP/Twig-Welt kommst
  • Freemarker: Für Email-Templates und Reports

Heute schauen wir uns alle Optionen an und du lernst, wann welche Engine die richtige Wahl ist!


📊 Server-Side vs. Client-Side Rendering – Kompletter Überblick

TechnologieRenderingPerformanceStartzeitSEOSyntaxSpring IntegrationCommunityUse Case
ThymeleafServer-SideMittel⚡⚡⚡ Schnell⭐⭐⭐⭐⭐ ExzellentHTML-ähnlich⭐⭐⭐⭐⭐ NativeSehr großStandard für Spring Boot Web-Apps
MustacheServer-SideHoch⚡⚡⚡ Schnell⭐⭐⭐⭐⭐ ExzellentMinimalistisch⭐⭐⭐⭐ StarterGroß (Multi-Language)Einfache Templates, Multi-Language
JTEServer-SideSehr hoch⚡⚡⚡ Schnell⭐⭐⭐⭐⭐ ExzellentJava-ähnlich⭐⭐⭐ ManualKlein aber aktivPerformance-kritische Apps
PebbleServer-SideHoch⚡⚡⚡ Schnell⭐⭐⭐⭐⭐ ExzellentTwig-ähnlich⭐⭐⭐⭐ StarterMittelPHP/Twig-Entwickler
GroovyServer-SideMittel⚡⚡⚡ Schnell⭐⭐⭐⭐⭐ ExzellentGroovy DSL⭐⭐⭐⭐ StarterMittelGroovy-Projekte
FreemarkerServer-SideMittel⚡⚡⚡ Schnell⭐⭐⭐⭐⭐ ExzellentFTL-Syntax⭐⭐⭐⭐ StarterGroßEmail, Reports, Legacy
JSPServer-SideNiedrig⚡⚡ Langsam⭐⭐⭐⭐ GutJava + HTML⭐⭐⭐ Built-inSehr groß (Legacy)Alte Java EE Apps
ReactClient-SideSehr hoch (nach Load)⚡ Sehr langsam⭐⭐ Schwierig (SSR nötig)JSX❌ Separate AppSehr großSPAs, hochinteraktive UIs
Vue.jsClient-Side / HybridHoch (nach Load)⚡ Langsam⭐⭐⭐ Mittel (SSR möglich)HTML-ähnlich❌ Separate AppGroßSPAs, Progressive Web Apps
AngularClient-SideHoch (nach Load)⚡ Sehr langsam⭐⭐ Schwierig (SSR nötig)TypeScript + HTML❌ Separate AppSehr großEnterprise SPAs

Legende:

Rendering:

  • Server-Side: HTML wird auf dem Server generiert → Schneller First Paint, besseres SEO
  • Client-Side: JavaScript generiert HTML im Browser → Langsamer Start, aber interaktiver

Startzeit (Time to First Contentful Paint):

  • ⚡⚡⚡ Schnell (~100-300ms): Server sendet fertiges HTML
  • ⚡⚡ Langsam (~800-1500ms): JavaScript muss erst laden
  • Sehr langsam (~1500-3000ms): Großes JS-Bundle muss laden und ausführen

SEO (Suchmaschinen-Optimierung):

  • ⭐⭐⭐⭐⭐ Exzellent: HTML sofort verfügbar, Google crawlt perfekt
  • ⭐⭐⭐⭐ Gut: Mit SSR möglich, aber Aufwand
  • ⭐⭐⭐ Mittel: SSR optional, funktioniert mit Nuxt.js/Next.js
  • ⭐⭐ Schwierig: Braucht SSR (Next.js/Angular Universal), sonst schlecht
  • Schlecht: Pure Client-Side, Google sieht leere Seite

🆚 Server-Side vs. Client-Side Rendering

Was ist der Unterschied?

Server-Side Rendering (SSR):

Browser → Server → Server generiert HTML → Browser zeigt HTML
                   (Thymeleaf, Mustache, etc.)

Client-Side Rendering (CSR):

Browser → Server → Server sendet leere HTML + JS-Bundle → 
                   Browser lädt JS → JS generiert HTML
                   (React, Vue, Angular)

Startzeit-Vergleich (Real-World Messung):

TechnologieTime to First PaintTime to InteractiveBundle Size
Thymeleaf~100ms~150ms0 KB (kein JS)
Mustache~80ms~120ms0 KB (kein JS)
JTE~50ms~90ms0 KB (kein JS)
React (CSR)~2000ms~3500ms150-300 KB
Vue.js (CSR)~1500ms~2800ms80-150 KB
Angular (CSR)~2500ms~4000ms200-400 KB
React + SSR (Next.js)~400ms~2000ms150-300 KB
Vue.js + SSR (Nuxt.js)~350ms~1800ms80-150 KB

Wichtig: Client-Side Frameworks sind NACH dem initialen Load sehr schnell und interaktiv. Der langsame Start ist nur beim ersten Laden der Seite!

SEO-Vergleich:

Server-Side Rendering (Thymeleaf, Mustache, etc.):

<!-- Was Google sieht: -->
<h1>Produktliste</h1>
<ul>
  <li>Produkt 1 - €29.99</li>
  <li>Produkt 2 - €49.99</li>
</ul>
✅ Google kann alles crawlen!

Client-Side Rendering ohne SSR (React, Vue, Angular):

<!-- Was Google sieht: -->
<div id="app"></div>
<script src="bundle.js"></script>
❌ Google sieht leere Seite!

Client-Side Rendering MIT SSR (Next.js, Nuxt.js):

<!-- Was Google sieht (First Load): -->
<h1>Produktliste</h1>
<ul>
  <li>Produkt 1 - €29.99</li>
  <li>Produkt 2 - €49.99</li>
</ul>
<script src="hydration.js"></script>
✅ Google kann crawlen, dann wird JS aktiviert!

🔥 Template Engine #1: Thymeleaf (Wiederholung)

Du kennst es bereits aus dem Basiskurs!

Schnelles Recap:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
    <h1 th:text="${user.name}">Default Name</h1>
    <ul>
        <li th:each="hobby : ${user.hobbies}" th:text="${hobby}">Hobby</li>
    </ul>
</body>
</html>

Was macht Thymeleaf besonders?

Natural Templates – HTML bleibt valide
Beste Spring Boot Integration
Umfangreiche Features (Security, i18n, Layouts)
Größte Community
Exzellente IDE-Unterstützung

❌ Etwas verbose Syntax
❌ Langsamer als kompilierte Engines
❌ Steile Lernkurve für komplexe Features

Wann Thymeleaf? Standard-Wahl für Spring Boot Web-Apps, Admin-Panels, Content-Management.


🔥 Template Engine #2: Mustache – Logic-less Templates

Philosophie: Keine Logik in Templates!

Mustache erzwingt strikte Trennung von Logik und Präsentation. Templates enthalten NULL Business-Logik.

Setup:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mustache</artifactId>
</dependency>

Controller:

@Controller
public class UserController {
    
    @GetMapping("/user")
    public String showUser(Model model) {
        User user = new User("Max Mustermann", 
            List.of("Coding", "Gaming", "Reading"));
        model.addAttribute("user", user);
        return "user"; // → templates/user.mustache
    }
}

Template: templates/user.mustache

<!DOCTYPE html>
<html>
<head>
    <title>User Profile</title>
</head>
<body>
    <h1>{{user.name}}</h1>
    
    <!-- Conditional -->
    {{#user.isActive}}
    <p>Status: ✅ Aktiv</p>
    {{/user.isActive}}
    
    {{^user.isActive}}
    <p>Status: ❌ Inaktiv</p>
    {{/user.isActive}}
    
    <!-- Loop -->
    <h2>Hobbies:</h2>
    <ul>
        {{#user.hobbies}}
        <li>{{.}}</li>
        {{/user.hobbies}}
    </ul>
    
    <!-- Partials (Fragments) -->
    {{> footer}}
</body>
</html>

Syntax-Erklärung:

SyntaxBedeutungBeispiel
{{variable}}Variable ausgeben{{user.name}}
{{#section}}...{{/section}}Section (Loop/Conditional){{#users}}...{{/users}}
{{^section}}...{{/section}}Inverted Section (else){{^error}}Kein Fehler{{/error}}
{{.}}Current item in loopIn {{#items}}{{.}}{{/items}}
{{> partial}}Include partial{{> header}}
{{! comment }}Kommentar{{! Dies ist ein Kommentar }}

Partials (Fragments): templates/footer.mustache

<footer>
    <p>&copy; 2025 Java Fleet Systems</p>
</footer>

Vorteile:

Extrem einfache Syntax – in 10 Minuten gelernt
Logic-less – erzwingt saubere Trennung
Multi-Language – Gleiche Templates in Java, JS, Ruby, etc.
Schnell – Minimaler Overhead
Portabel – Templates funktionieren überall

Nachteile:

Keine Logik – kann limitierend sein
Weniger Features als Thymeleaf
Komplexe UIs schwierig – verschachtelte Strukturen umständlich
Keine i18n-Unterstützung out-of-the-box

Wann Mustache?

  • ✅ Du willst strikte Trennung von Logik und Presentation
  • ✅ Templates sollen in mehreren Sprachen wiederverwendet werden (Frontend + Backend)
  • ✅ Sehr einfache, statische Seiten
  • ✅ Team aus verschiedenen Tech-Stacks (Java, Node.js, etc.)

Real-World Beispiel: Email-Templates, die sowohl im Backend (Java) als auch Frontend (Node.js) gerendert werden.


🔥 Template Engine #3: JTE – Java Template Engine (High Performance)

Philosophie: Kompilierte, Type-Safe Templates!

JTE kompiliert Templates zu Java-Bytecode. Das bedeutet:

  • 🚀 Maximale Performance
  • 🛡️ Type-Safety zur Compile-Zeit
  • Fehler vor Deployment

Setup:

<dependency>
    <groupId>gg.jte</groupId>
    <artifactId>jte-spring-boot-starter-3</artifactId>
    <version>3.1.9</version>
</dependency>

application.properties:

gg.jte.development-mode=true
gg.jte.use-precompiled-templates=false

User Model:

public record User(String name, List<String> hobbies, boolean isActive) {
}

Controller:

@Controller
public class UserController {
    
    @GetMapping("/user")
    public String showUser(Model model) {
        User user = new User("Max Mustermann", 
            List.of("Coding", "Gaming"), true);
        model.addAttribute("user", user);
        return "user"; // → templates/user.jte
    }
}

Template: src/main/jte/user.jte

@import de.example.model.User

@param User user

<!DOCTYPE html>
<html>
<head>
    <title>User Profile</title>
</head>
<body>
    <h1>${user.name()}</h1>
    
    @if(user.isActive())
        <p>Status: ✅ Aktiv</p>
    @else
        <p>Status: ❌ Inaktiv</p>
    @endif
    
    <h2>Hobbies:</h2>
    <ul>
        @for(String hobby : user.hobbies())
            <li>${hobby}</li>
        @endfor
    </ul>
    
    @template.layout.main(
        content = @`
            <p>Custom content here</p>
        `
    )
</body>
</html>

Syntax-Erklärung:

SyntaxBedeutungBeispiel
@importJava-Import@import de.example.User
@paramParameter-Deklaration (Type-safe!)@param User user
${expression}Variable ausgeben (escaped)${user.name()}
$unsafe{expression}Unescaped HTML$unsafe{richText}
@if()...@endifConditional@if(user.isActive())...@endif
@for()...@endforLoop@for(var item : items)...@endfor
@template.name()Template aufrufen@template.layout.main()
@…„Content BlockFür Layout-Parameter

Layout-Template: src/main/jte/layout/main.jte

@import gg.jte.Content

@param Content content

<!DOCTYPE html>
<html>
<head>
    <title>My App</title>
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>
    <nav>
        <a href="/">Home</a>
        <a href="/users">Users</a>
    </nav>
    
    <main>
        ${content}
    </main>
    
    <footer>
        &copy; 2025 Java Fleet
    </footer>
</body>
</html>

Verwendung im Template:

@import de.example.model.User
@param User user

@template.layout.main(
    content = @`
        <h1>${user.name()}</h1>
        <p>Dies wird im Layout gerendert!</p>
    `
)

Vorteile:

EXTREM schnell – bis zu 3x schneller als Thymeleaf
Type-safe – Compiler prüft alle Typen
Fehler zur Compile-Zeit – nicht erst in Production
IDE-Support – Autocompletion für alle Parameter
Hot-Reload im Dev-Mode
Saubere Syntax – ähnlich wie JSP, aber moderner

Nachteile:

Kleinere Community – weniger Tutorials/Beispiele
Compile-Schritt nötig – Templates müssen kompiliert werden
Eigene Syntax – nicht HTML-nativ wie Thymeleaf
Mehr Boilerplate@import und @param für jedes Template

Performance-Vergleich:

Benchmark: 1000 Requests, komplexe Seite mit Listen

JTE:        47ms  ████████████████████
Pebble:     89ms  ██████████
Mustache:   95ms  █████████
Thymeleaf:  156ms ████

Wann JTE?

  • Performance ist kritisch (High-Traffic Apps)
  • ✅ Du willst Type-Safety zur Compile-Zeit
  • ✅ Große Enterprise-Apps mit vielen Templates
  • ✅ Team hat Java-Fokus (keine Designer)

Real-World Beispiel: E-Commerce-Plattform mit Millionen Page-Views pro Tag.


🔥 Template Engine #4: Pebble – Twig für Java

Philosophie: Flexibel, schnell und vertraut!

Pebble orientiert sich an Twig (PHP) und Jinja (Python). Perfekt für Teams, die aus der PHP-Welt kommen!

Setup:

<dependency>
    <groupId>io.pebbletemplates</groupId>
    <artifactId>pebble-spring-boot-starter</artifactId>
    <version>3.2.2</version>
</dependency>

application.properties:

pebble.suffix=.pebble
pebble.cache=true

Controller:

@Controller
public class UserController {
    
    @GetMapping("/user")
    public String showUser(Model model) {
        User user = new User("Max Mustermann", 
            List.of("Coding", "Gaming"), true);
        model.addAttribute("user", user);
        return "user"; // → templates/user.pebble
    }
}

Template: templates/user.pebble

{# templates/user.pebble #}
<!DOCTYPE html>
<html>
<head>
    <title>User Profile</title>
</head>
<body>
    <h1>{{ user.name }}</h1>
    
    {% if user.isActive %}
        <p>Status: ✅ Aktiv</p>
    {% else %}
        <p>Status: ❌ Inaktiv</p>
    {% endif %}
    
    <h2>Hobbies:</h2>
    <ul>
        {% for hobby in user.hobbies %}
            <li>{{ hobby }}</li>
        {% endfor %}
    </ul>
    
    {# Filter verwenden #}
    <p>Name in Uppercase: {{ user.name | upper }}</p>
    <p>Anzahl Hobbies: {{ user.hobbies | length }}</p>
</body>
</html>

Template Inheritance (extends):

Base Layout: templates/layout.pebble

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}Default Title{% endblock %}</title>
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>
    <nav>
        <a href="/">Home</a>
        <a href="/users">Users</a>
    </nav>
    
    <main>
        {% block content %}
            Default content
        {% endblock %}
    </main>
    
    <footer>
        {% block footer %}
            &copy; 2025 Java Fleet
        {% endblock %}
    </footer>
</body>
</html>

Child Template:

{% extends "layout.pebble" %}

{% block title %}User Profile{% endblock %}

{% block content %}
    <h1>{{ user.name }}</h1>
    <p>Hobbies: {{ user.hobbies | join(", ") }}</p>
{% endblock %}

Syntax-Erklärung:

SyntaxBedeutungBeispiel
{{ variable }}Variable ausgeben{{ user.name }}
{% if %}...{% endif %}Conditional{% if user.isActive %}...{% endif %}
{% for %}...{% endfor %}Loop{% for hobby in hobbies %}...{% endfor %}
{% extends %}Template Inheritance{% extends "layout.pebble" %}
{% block %}Block definieren{% block content %}...{% endblock %}
{% include %}Include Template{% include "header.pebble" %}
{# comment #}Kommentar{# Dies ist ein Kommentar #}
{{ var | filter }}Filter anwenden{{ name | upper }}

Wichtige Filter:

FilterBeschreibungBeispiel
upperUppercase{{ name | upper }} → „MAX“
lowerLowercase{{ name | lower }} → „max“
capitalizeErster Buchstabe groß{{ name | capitalize }}
lengthLänge{{ items | length }} → 5
defaultDefault-Wert{{ name | default("Guest") }}
dateDatum formatieren{{ now | date("dd.MM.yyyy") }}
joinArray joinen{{ items | join(", ") }}
sliceArray slicen{{ items | slice(0, 3) }}

Macros (wiederverwendbare Komponenten):

{# macros/forms.pebble #}
{% macro input(name, label, type="text") %}
    <div class="form-group">
        <label for="{{ name }}">{{ label }}</label>
        <input type="{{ type }}" 
               id="{{ name }}" 
               name="{{ name }}" 
               class="form-control">
    </div>
{% endmacro %}

Verwendung:

{% import "macros/forms.pebble" as forms %}

<form>
    {{ forms.input("username", "Benutzername") }}
    {{ forms.input("email", "E-Mail", "email") }}
    {{ forms.input("password", "Passwort", "password") }}
</form>

Vorteile:

Vertraute Syntax für PHP/Python-Entwickler
Sehr schnell – eingebautes Caching
Template Inheritance – DRY-Prinzip
Viele Filter – Daten-Transformation im Template
Macros – Wiederverwendbare Komponenten
Gute Balance zwischen Features und Einfachheit

Nachteile:

Kleinere Community als Thymeleaf
Weniger Spring-spezifische Features
Keine Natural Templates – nicht wie HTML aussehend
IDE-Support schwächer als bei Thymeleaf

Wann Pebble?

  • ✅ Team kommt aus PHP/Twig oder Python/Jinja
  • ✅ Du willst Template Inheritance (extends)
  • Performance ist wichtig, aber nicht kritisch
  • ✅ Du magst die Twig-Syntax mehr als Thymeleaf

Real-World Beispiel: Migration von Symfony (PHP/Twig) zu Spring Boot.


🔥 Template Engine #5: Groovy Templates – DSL Power

Philosophie: Volle Programmiersprache in Templates!

Groovy Templates nutzen Groovy als DSL für HTML-Generierung. Volle Groovy-Power in Templates!

Setup:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-groovy-templates</artifactId>
</dependency>

application.properties:

spring.groovy.template.suffix=.tpl
spring.groovy.template.cache=true

Controller:

@Controller
public class UserController {
    
    @GetMapping("/user")
    public String showUser(Model model) {
        User user = new User("Max Mustermann", 
            List.of("Coding", "Gaming"), true);
        model.addAttribute("user", user);
        return "user"; // → templates/user.tpl
    }
}

Template: templates/user.tpl

yieldUnescaped '<!DOCTYPE html>'
html {
    head {
        title('User Profile')
        link(rel: 'stylesheet', href: '/css/style.css')
    }
    body {
        h1(user.name)
        
        // Conditional mit normalem Groovy-Code
        if (user.isActive) {
            p('Status: ✅ Aktiv')
        } else {
            p('Status: ❌ Inaktiv')
        }
        
        h2('Hobbies:')
        ul {
            user.hobbies.each { hobby ->
                li(hobby)
            }
        }
        
        // Volle Groovy-Power!
        div(class: 'info') {
            p("Anzahl Hobbies: ${user.hobbies.size()}")
            p("Name uppercase: ${user.name.toUpperCase()}")
        }
    }
}

Layout mit include:

Layout: templates/layout.tpl

yieldUnescaped '<!DOCTYPE html>'
html {
    head {
        title(pageTitle ?: 'Default Title')
        link(rel: 'stylesheet', href: '/css/style.css')
    }
    body {
        nav {
            a(href: '/', 'Home')
            a(href: '/users', 'Users')
        }
        
        main {
            // Content wird hier eingefügt
            mainContent()
        }
        
        footer {
            p('© 2025 Java Fleet')
        }
    }
}

Verwendung:

layout 'layout.tpl',
    pageTitle: 'User Profile',
    mainContent: {
        h1(user.name)
        p("Hobbies: ${user.hobbies.join(', ')}")
    }

Vorteile:

Volle Groovy-Power – jede Groovy-Funktion nutzbar
Type-safe – Compiler prüft Syntax
Gut für Groovy-Entwickler – vertraute Syntax
Flexibel – keine Template-Sprache-Limitierungen
DSL-basiert – sehr mächtig

Nachteile:

Steile Lernkurve für Nicht-Groovy-Entwickler
Keine HTML-Syntax – sieht nicht wie HTML aus
Kleinere Community
Schwer für Designer – braucht Programmierkenntnisse
Performance schlechter als JTE

Wann Groovy Templates?

  • ✅ Dein Projekt ist bereits Groovy-basiert
  • ✅ Team kennt Groovy gut
  • ✅ Du willst maximale Flexibilität
  • ✅ Du brauchst komplexe Logik in Templates

Real-World Beispiel: Grails-Migration zu Spring Boot.


🔥 Template Engine #6: Freemarker – Der Allrounder

Philosophie: Flexibel, bewährt, vielseitig!

Freemarker ist eine der ältesten und bewährtesten Template Engines. Besonders stark für Email-Templates und Reports.

Setup:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

application.properties:

spring.freemarker.suffix=.ftl
spring.freemarker.cache=true

Controller:

@Controller
public class UserController {
    
    @GetMapping("/user")
    public String showUser(Model model) {
        User user = new User("Max Mustermann", 
            List.of("Coding", "Gaming"), true);
        model.addAttribute("user", user);
        return "user"; // → templates/user.ftl
    }
}

Template: templates/user.ftl

<!DOCTYPE html>
<html>
<head>
    <title>User Profile</title>
</head>
<body>
    <h1>${user.name}</h1>
    
    <#if user.isActive>
        <p>Status: ✅ Aktiv</p>
    <#else>
        <p>Status: ❌ Inaktiv</p>
    </#if>
    
    <h2>Hobbies:</h2>
    <ul>
        <#list user.hobbies as hobby>
            <li>${hobby}</li>
        </#list>
    </ul>
    
    <#-- Built-in Functions -->
    <p>Name uppercase: ${user.name?upper_case}</p>
    <p>Anzahl Hobbies: ${user.hobbies?size}</p>
    
    <#-- Includes -->
    <#include "footer.ftl">
</body>
</html>

Syntax-Erklärung:

SyntaxBedeutungBeispiel
${variable}Variable ausgeben${user.name}
<#if>...</#if>Conditional<#if user.isActive>...</#if>
<#list>...</#list>Loop<#list items as item>...</#list>
<#include>Include Template<#include "header.ftl">
<#assign>Variable definieren<#assign total = price * quantity>
<#macro>...</#macro>Macro definieren<#macro greeting name>Hello ${name}</#macro>
<#-- comment -->Kommentar<#-- Dies ist ein Kommentar -->
${var?function}Built-in Function${name?upper_case}

Built-in Functions:

FunctionBeschreibungBeispiel
?upper_caseUppercase${name?upper_case}
?lower_caseLowercase${name?lower_case}
?capitalizeCapitalize${name?capitalize}
?sizeGröße/Länge${items?size}
?default("fallback")Default-Wert${name?default("Guest")}
?dateDatum formatieren${now?date("dd.MM.yyyy")}
?join(", ")Array joinen${items?join(", ")}
?has_contentPrüft ob leer<#if name?has_content>...</#if>

Macros (wiederverwendbare Komponenten):

<#-- macros/forms.ftl -->
<#macro input name label type="text">
    <div class="form-group">
        <label for="${name}">${label}</label>
        <input type="${type}" 
               id="${name}" 
               name="${name}" 
               class="form-control">
    </div>
</#macro>

Verwendung:

<#import "macros/forms.ftl" as forms>

<form>
    <@forms.input name="username" label="Benutzername" />
    <@forms.input name="email" label="E-Mail" type="email" />
    <@forms.input name="password" label="Passwort" type="password" />
</form>

Email-Template Beispiel:

<#-- Email für Passwort-Reset -->
<!DOCTYPE html>
<html>
<body>
    <h1>Passwort zurücksetzen</h1>
    
    <p>Hallo ${user.name},</p>
    
    <p>Du hast eine Anfrage zum Zurücksetzen deines Passworts gestellt.</p>
    
    <p>
        <a href="${resetUrl}">Passwort jetzt zurücksetzen</a>
    </p>
    
    <p>Dieser Link ist gültig bis: ${expiryDate?date("dd.MM.yyyy HH:mm")}</p>
    
    <p>Wenn du diese Anfrage nicht gestellt hast, ignoriere diese E-Mail.</p>
    
    <#-- Footer -->
    <hr>
    <p style="color: #666; font-size: 12px;">
        Diese E-Mail wurde automatisch generiert. Bitte nicht antworten.
    </p>
</body>
</html>

Vorteile:

Sehr bewährt – seit Jahren in Production
Perfekt für Emails – HTML + Text in einem Template
Gute Dokumentation – umfangreich
Viele Built-in Functions – weniger Custom-Code nötig
Macros – Wiederverwendbarkeit
Große Community

Nachteile:

Veraltetes Design – nicht modern
Performance nur Mittelfeld
Syntax gewöhnungsbedürftig
Keine Natural Templates

Wann Freemarker?

  • Email-Templates (HTML + Text)
  • Reports generieren (PDF via HTML)
  • Legacy-Projekte migrieren
  • ✅ Team hat Freemarker-Erfahrung

Real-World Beispiel: Newsletter-System mit personalisierten Emails.


🌐 JavaScript Frameworks: React, Vue.js & Angular

Du kannst die Beispiele hier auf meinem Acount bei github herunterladen;

Warum JavaScript Frameworks im Vergleich?

Du fragst dich vielleicht: „Warum schauen wir uns React, Vue und Angular in einem Server-Side Template Engine Kurs an?“

Gute Frage! Weil du als Spring Boot Developer oft vor der Entscheidung stehst:

Option 1: Server-Side Rendering (Thymeleaf, Mustache, etc.)
Option 2: API + JavaScript Framework (React, Vue, Angular)

Beide Ansätze sind legitim – aber für unterschiedliche Use Cases!


🔥 JavaScript Framework #1: React – Das UI-Monster

Architektur mit Spring Boot:

Browser                    Spring Boot Backend
  ↓                              ↓
React App    ←--REST API-->  @RestController
(Port 3000)                   (Port 8080)

Zwei separate Projekte!

Spring Boot Backend:

@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "http://localhost:3000")
public class UserController {
    
    private final UserService userService;
    
    @GetMapping
    public List<UserDTO> getAllUsers() {
        return userService.findAll();
    }
    
    @GetMapping("/{id}")
    public UserDTO getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
    
    @PostMapping
    public UserDTO createUser(@RequestBody @Valid UserDTO user) {
        return userService.create(user);
    }
}

React Frontend (JSX):

// UserList.jsx
import { useState, useEffect } from 'react';

function UserList() {
    const [users, setUsers] = useState([]);
    const [loading, setLoading] = useState(true);
    
    useEffect(() => {
        fetch('http://localhost:8080/api/users')
            .then(response => response.json())
            .then(data => {
                setUsers(data);
                setLoading(false);
            });
    }, []);
    
    if (loading) return <div>Loading...</div>;
    
    return (
        <div>
            <h1>User List</h1>
            <ul>
                {users.map(user => (
                    <li key={user.id}>
                        {user.name} - {user.email}
                    </li>
                ))}
            </ul>
        </div>
    );
}

export default UserList;

Setup:

Backend: Spring Boot mit @RestController
Frontend: Create React App oder Vite

# React App erstellen
npx create-react-app frontend
cd frontend
npm start

Vorteile:

Hochinteraktive UIs – Real-time Updates ohne Page-Reload
Component-basiert – Wiederverwendbare UI-Komponenten
Große Community – Millionen Entwickler, unzählige Libraries
Mobile Apps – Mit React Native auch für iOS/Android
Performance nach Load – Sehr schnelle Interaktionen
Separate Deployments – Frontend und Backend unabhängig

Nachteile:

Langsamer Start – 2-3 Sekunden bis Content sichtbar
SEO problematisch – Braucht Next.js für SSR
Zwei Projekte – Separate Codebases, mehr Komplexität
Lernkurve – JavaScript, JSX, Hooks, State Management
CORS-Probleme – Cross-Origin muss konfiguriert werden
Deployment komplexer – Zwei separate Deployments

Wann React mit Spring Boot?

Hochinteraktive Dashboards (Charts, Echtzeit-Updates)
Single Page Applications (Gmail-ähnlich)
Mobile + Web gemeinsam (React + React Native)
Separate Frontend-Team vorhanden
API-First Architektur gewünscht

Real-World Beispiel: Trading-Dashboard mit Live-Charts und Real-time Updates.


🔥 JavaScript Framework #2: Vue.js – Das Progressive Framework

Philosophie: Incrementally Adoptable!

Vue.js kannst du schrittweise einführen – von einfachen Widgets bis zur kompletten SPA!

Option 1: Vue.js als Widget in Thymeleaf (Hybrid)

Thymeleaf Template:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>User List</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
</head>
<body>
    <h1>User List</h1>
    
    <!-- Vue.js Widget für interaktive Suche -->
    <div id="app">
        <input v-model="searchTerm" placeholder="Suche...">
        <ul>
            <li v-for="user in filteredUsers" :key="user.id">
                {{ user.name }} - {{ user.email }}
            </li>
        </ul>
    </div>
    
    <script>
        const { createApp } = Vue;
        
        createApp({
            data() {
                return {
                    searchTerm: '',
                    users: []
                }
            },
            computed: {
                filteredUsers() {
                    return this.users.filter(user => 
                        user.name.toLowerCase().includes(this.searchTerm.toLowerCase())
                    );
                }
            },
            mounted() {
                fetch('/api/users')
                    .then(r => r.json())
                    .then(data => this.users = data);
            }
        }).mount('#app');
    </script>
</body>
</html>

Das Beste aus beiden Welten! Server-Side Rendering + interaktive Widgets!

Option 2: Vue.js als vollständige SPA

Spring Boot Backend:

@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "http://localhost:5173")
public class UserController {
    
    @GetMapping
    public List<UserDTO> getAllUsers() {
        return userService.findAll();
    }
}

Vue.js Frontend:

<!-- UserList.vue -->
<template>
    <div>
        <h1>User List</h1>
        <input v-model="searchTerm" placeholder="Suche..." />
        <ul>
            <li v-for="user in filteredUsers" :key="user.id">
                {{ user.name }} - {{ user.email }}
            </li>
        </ul>
    </div>
</template>

<script setup>
import { ref, computed, onMounted } from 'vue';

const searchTerm = ref('');
const users = ref([]);

const filteredUsers = computed(() => {
    return users.value.filter(user => 
        user.name.toLowerCase().includes(searchTerm.value.toLowerCase())
    );
});

onMounted(async () => {
    const response = await fetch('http://localhost:8080/api/users');
    users.value = await response.json();
});
</script>

Setup:

# Vue.js App erstellen
npm create vue@latest
cd frontend
npm install
npm run dev

Vorteile:

Progressive – Von Widget bis SPA flexibel
Einfacher als React – Niedrigere Lernkurve
Single-File Components – HTML, CSS, JS in einer Datei
Exzellente Dokumentation – Sehr anfängerfreundlich
Kleineres Bundle – Schneller als React/Angular
SSR mit Nuxt.js – SEO-freundlich möglich

Nachteile:

Kleinere Community als React
Weniger Jobs – React dominiert den Markt
SEO ohne SSR problematisch
Zwei Projekte nötig für SPA

Wann Vue.js mit Spring Boot?

Hybrid-Ansatz – Thymeleaf + interaktive Widgets
Progressive Enhancement – Schrittweise mehr Interaktivität
Kleinere Teams – Einfacher zu lernen als React
Moderate Interaktivität – Nicht extrem komplex

Real-World Beispiel: Admin-Panel mit Thymeleaf, aber interaktive Charts mit Vue.js.


🔥 JavaScript Framework #3: Angular – Enterprise-Monolith

Philosophie: Alles inklusive!

Angular ist ein komplettes Framework – nicht nur eine Library. Router, HTTP, Forms, Testing – alles dabei!

Spring Boot Backend:

@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "http://localhost:4200")
public class UserController {
    
    @GetMapping
    public List<UserDTO> getAllUsers() {
        return userService.findAll();
    }
}

Angular Frontend:

user.service.ts:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export interface User {
    id: number;
    name: string;
    email: string;
}

@Injectable({
    providedIn: 'root'
})
export class UserService {
    private apiUrl = 'http://localhost:8080/api/users';
    
    constructor(private http: HttpClient) {}
    
    getUsers(): Observable<User[]> {
        return this.http.get<User[]>(this.apiUrl);
    }
}

user-list.component.ts:

import { Component, OnInit } from '@angular/core';
import { UserService, User } from './user.service';

@Component({
    selector: 'app-user-list',
    templateUrl: './user-list.component.html',
    styleUrls: ['./user-list.component.css']
})
export class UserListComponent implements OnInit {
    users: User[] = [];
    loading = true;
    
    constructor(private userService: UserService) {}
    
    ngOnInit(): void {
        this.userService.getUsers().subscribe({
            next: (data) => {
                this.users = data;
                this.loading = false;
            },
            error: (err) => console.error(err)
        });
    }
}

user-list.component.html:

<div *ngIf="loading">Loading...</div>

<div *ngIf="!loading">
    <h1>User List</h1>
    <ul>
        <li *ngFor="let user of users">
            {{ user.name }} - {{ user.email }}
        </li>
    </ul>
</div>

Setup:

# Angular App erstellen
npm install -g @angular/cli
ng new frontend
cd frontend
ng serve

Vorteile:

Komplett-Lösung – Alles eingebaut (Router, HTTP, Forms, etc.)
TypeScript – Type-Safety, bessere IDE-Unterstützung
Enterprise-ready – Klare Strukturen, große Teams
RxJS – Reactive Programming für komplexe Datenflüsse
Angular Universal – SSR für SEO möglich
CLI-Power – Code-Generation, Testing, Building

Nachteile:

Sehr steile Lernkurve – RxJS, TypeScript, Decorators
Langsamer Start – Größtes Bundle (200-400 KB)
Overkill für kleine Projekte
Komplexität – Viele Konzepte zu lernen
Breaking Changes – Upgrades können schmerzhaft sein

Wann Angular mit Spring Boot?

Große Enterprise-Projekte (>10 Entwickler)
Klare Strukturen gewünscht
TypeScript im Team etabliert
Komplexe Business-Logik im Frontend
Long-term Projekt (5+ Jahre)

Real-World Beispiel: ERP-System mit komplexen Workflows und vielen Formularen.


🆚 Entscheidungsbaum: Server-Side vs. Client-Side

Stelle dir diese Fragen:

1. Wie interaktiv muss die UI sein?

Statisch (Blog, Docs)           → Thymeleaf/Mustache
Wenig interaktiv (Admin-Panel)  → Thymeleaf + Vue.js Widgets
Mittel interaktiv (Dashboard)   → Vue.js SPA
Sehr interaktiv (Gmail-like)    → React SPA

2. Wie wichtig ist SEO?

Kritisch (E-Commerce, Blog)     → Server-Side (Thymeleaf, JTE)
Wichtig (Marketing-Site)        → Server-Side oder SSR (Next.js, Nuxt.js)
Egal (Interne Tools, Dashboards)→ Client-Side (React, Vue, Angular)

3. Wie groß ist das Team?

1-2 Entwickler                  → Thymeleaf oder Vue.js
3-5 Entwickler                  → Vue.js oder React
5-10 Entwickler                 → React oder Angular
10+ Entwickler                  → Angular (Enterprise)

4. Brauchst du Mobile Apps?

Nein                            → Beliebige Option
Ja, später vielleicht           → React (React Native später möglich)
Ja, sofort                      → React + React Native

5. Wie wichtig ist Time-to-Market?

Sehr schnell (1-2 Monate)       → Thymeleaf (keine zwei Projekte)
Normal (3-6 Monate)             → Vue.js oder React
Egal (6+ Monate)                → Angular (komplette Lösung)

📊 Die ultimative Entscheidungstabelle

Use CaseEmpfehlungGrund
Blog / Content-SiteThymeleaf / JTESEO kritisch, statisch
E-Commerce ShopThymeleaf + Vue WidgetsSEO + etwas Interaktivität
Admin-Panel (intern)Thymeleaf oder Vue.jsSchnell gebaut, SEO egal
Dashboard (Charts)React oder Vue.jsHochinteraktiv, Echtzeit
CRM-SystemAngularEnterprise, komplex
Social Media AppReactHochinteraktiv, Mobile später
Email-Client (Gmail-like)ReactMaximale Interaktivität
Banking-AppAngularSecurity, Enterprise
Startup MVPThymeleaf oder Vue.jsSchnell, flexibel
API-First ArchitekturReact / Vue / AngularBackend = API only

💡 Elyndra’s Hybrid-Empfehlung

Mein Favorit für die meisten Projekte:

Thymeleaf + Vue.js Widgets = Best of Both Worlds!

Warum?

  1. SEO funktioniert – Server-Side Rendering
  2. Schneller Start – Kein großes JS-Bundle
  3. Interaktivität wo nötig – Vue.js für einzelne Komponenten
  4. Ein Projekt – Nicht zwei separate Codebases
  5. Einfach zu deployen – Ein JAR/WAR
  6. Progressive – Später zu SPA migrieren möglich

Beispiel:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Product List</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@3"></script>
</head>
<body>
    <!-- Static Header (Thymeleaf) -->
    <header>
        <h1>Our Products</h1>
        <nav>
            <a href="/">Home</a>
            <a href="/products">Products</a>
        </nav>
    </header>
    
    <!-- Interactive Search (Vue.js) -->
    <div id="product-search">
        <input v-model="search" placeholder="Search products...">
        <div v-for="product in filteredProducts" :key="product.id">
            <h3>{{ product.name }}</h3>
            <p>{{ product.price }}</p>
        </div>
    </div>
    
    <!-- Static Footer (Thymeleaf) -->
    <footer th:replace="~{fragments/footer :: footer}"></footer>
    
    <script>
        Vue.createApp({
            data() {
                return { search: '', products: [] }
            },
            computed: {
                filteredProducts() {
                    return this.products.filter(p => 
                        p.name.toLowerCase().includes(this.search.toLowerCase())
                    );
                }
            },
            mounted() {
                fetch('/api/products').then(r => r.json()).then(data => this.products = data);
            }
        }).mount('#product-search');
    </script>
</body>
</html>

Das Beste aus beiden Welten!


🔥 Häufige Fehler

Fehler #1: „Alles muss eine SPA sein!“

Falsch: Jedes Projekt als React SPA bauen
Richtig: Passende Technologie für den Use Case wählen

80% der Web-Apps brauchen KEINE SPA!

Fehler #2: „Server-Side ist veraltet!“

Falsch: Thymeleaf ist old-school
Richtig: Server-Side ist in vielen Fällen besser (SEO, Performance, Einfachheit)

Beispiel: GitHub nutzt (teilweise) Server-Side Rendering – und die sind nicht dumm!

Fehler #3: „SEO mit JavaScript geht nicht!“

Falsch: SPAs können nicht SEO
Richtig: SPAs brauchen SSR (Next.js, Nuxt.js, Angular Universal)

Aber: SSR ist komplex und teuer. Wenn möglich, Server-Side Rendering nutzen!

Fehler #4: „Ein Framework für alles!“

Falsch: „Wir sind ein React-Shop, alles wird React!“
Richtig: Jedes Projekt individuell bewerten

Beispiel: Admin-Panel mit Thymeleaf, Kunden-Dashboard mit React – völlig OK!


🎯 Praktische Übung (2h)

Baue die gleiche User-Liste mit 4 verschiedenen Ansätzen:

  1. Thymeleaf – Pure Server-Side
  2. Thymeleaf + Vue.js Widget – Hybrid
  3. React SPA – Pure Client-Side
  4. Vue.js SPA – Pure Client-Side

Aufgabe:

  • Liste von Users anzeigen
  • Suchfunktion
  • Performance messen (Time to First Paint)
  • SEO testen (Google Lighthouse)

Vergleiche:

  • Startzeit
  • Bundle-Size
  • Entwicklungszeit
  • SEO-Score

🏆 Fazit: Wann welche Technologie?

Nimm Server-Side (Thymeleaf, JTE, Pebble) wenn:

  • ✅ SEO kritisch ist
  • ✅ Schnelle Ladezeit wichtig ist
  • ✅ Wenig JavaScript-Know-how im Team
  • ✅ Content-fokussierte Seiten
  • ✅ Einfaches Deployment gewünscht

Nimm Hybrid (Thymeleaf + Vue.js) wenn:

  • ✅ SEO + Interaktivität beides wichtig
  • ✅ Progressive Enhancement gewünscht
  • ✅ Ein Projekt, nicht zwei
  • ✅ Moderate Interaktivität nötig

Nimm Client-Side (React, Vue, Angular) wenn:

  • ✅ Hochinteraktive UIs nötig
  • ✅ SEO nicht wichtig (oder SSR machbar)
  • ✅ Mobile App später auch gewünscht
  • ✅ API-First Architektur
  • ✅ Separate Frontend-Team

Merke: Es gibt keine „beste“ Technologie – nur die beste für deinen Use Case!


Warum JSP noch erwähnen?

JSP (JavaServer Pages) ist veraltet, aber du wirst es in Legacy-Projekten noch oft sehen. Daher solltest du es kennen.

Setup:

<!-- JSP Support -->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>

<!-- JSTL -->
<dependency>
    <groupId>jakarta.servlet.jsp.jstl</groupId>
    <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
</dependency>
<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>jakarta.servlet.jsp.jstl</artifactId>
</dependency>

application.properties:

spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

Template: WEB-INF/views/user.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
    <title>User Profile</title>
</head>
<body>
    <h1>${user.name}</h1>
    
    <c:if test="${user.isActive}">
        <p>Status: ✅ Aktiv</p>
    </c:if>
    
    <c:if test="${!user.isActive}">
        <p>Status: ❌ Inaktiv</p>
    </c:if>
    
    <h2>Hobbies:</h2>
    <ul>
        <c:forEach items="${user.hobbies}" var="hobby">
            <li>${hobby}</li>
        </c:forEach>
    </ul>
    
    <%-- Include --%>
    <jsp:include page="footer.jsp" />
</body>
</html>

Warum JSP vermeiden?

Veraltet – nicht mehr empfohlen
Langsam – schlechte Performance
Schwer testbar – benötigt Servlet-Container
Nicht für Spring Boot gemacht
Deployment kompliziert – JAR-Packaging problematisch

Wann JSP (trotzdem)?

  • ⚠️ Legacy-Migration – existierende JSP-App zu Spring Boot migrieren
  • ⚠️ Team kennt nur JSP – Übergangsphase

Empfehlung: Migriere zu Thymeleaf oder einer modernen Engine!


📊 Template Engines – Die Entscheidungsmatrix

Welche Engine für welchen Use Case?

Use CaseEmpfohlene EngineGrund
Standard Spring Boot Web-AppThymeleafBeste Integration, große Community
High-Performance AppJTEKompiliert, sehr schnell
Email-SystemFreemarker / MustacheBewährt für Emails
Multi-Language TemplatesMustacheFunktioniert in Java, JS, Ruby, etc.
PHP/Twig BackgroundPebbleVertraute Syntax
Groovy-ProjektGroovy TemplatesNative Integration
Maximale EinfachheitMustacheLogic-less, minimalistisch
Complex Enterprise UIThymeleafUmfangreichste Features
Legacy-MigrationJSP → ThymeleafModerner Stack
Admin-Panel internThymeleaf / PebbleSchnell, einfach
API-First + minimale UIMustacheLeichtgewichtig

Performance-Ranking:

1. JTE          ~47ms  ████████████████████ (Kompiliert, type-safe)
2. Pebble       ~89ms  ██████████           (Caching, schnell)
3. Mustache     ~95ms  █████████            (Minimal, einfach)
4. Freemarker   ~128ms ██████               (Bewährt)
5. Thymeleaf    ~156ms ████                 (Feature-reich)
6. Groovy       ~180ms ███                  (Dynamisch, flexibel)
7. JSP          ~220ms ██                   (Legacy, langsam)

Wichtig: In 90% der Fälle ist Performance KEIN Entscheidungskriterium. Wähle nach Team-Skills und Features!


💡 Elyndra’s Empfehlungen

Für neue Projekte:

1. Standard-Wahl: Thymeleaf

  • Wenn du unsicher bist, nimm Thymeleaf
  • Größte Community, beste Docs
  • Alle Features out-of-the-box

2. Performance-kritisch: JTE

  • E-Commerce, High-Traffic
  • Type-Safety wichtig
  • Team mit Java-Fokus

3. Einfachheit gewünscht: Mustache

  • Templates sollen simpel bleiben
  • Multi-Language Setup
  • Keine komplexen Features nötig

Für Legacy-Projekte:

Migration von JSP: → Thymeleaf
Migration von PHP/Twig: → Pebble
Migration von Groovy/Grails: → Groovy Templates

Für spezielle Use Cases:

Email-System: Freemarker oder Mustache
Reports: Freemarker
API-First mit minimal UI: Mustache


🎯 Praktische Übung (1h)

Baue die gleiche User-Liste mit 3 verschiedenen Template Engines:

  1. Mustache – Logic-less
  2. JTE – High-Performance
  3. Pebble – Flexible Syntax

Aufgabe:

  • Liste von Users anzeigen
  • Filter: Nur aktive Users
  • Sortierung nach Name
  • Include Footer-Fragment

Bonus:

  • Performance messen mit JMeter
  • Vergleich der Syntax-Komplexität

Pause! ☕ Du hast jetzt alle Template Engines verglichen! Zeit für eine Mittagspause, dann geht’s weiter mit Microservices.


🌐 Teil 2: Microservices Grundlagen (Nachmittag, 4h)

Warum Microservices?

Stell dir vor, deine App wird riesig. Statt eines monolithischen Projekts mit 100.000 Zeilen Code kannst du sie in kleine, unabhängige Services aufteilen:

  • User-Service: Verwaltet User
  • Order-Service: Verwaltet Bestellungen
  • Payment-Service: Verwaltet Zahlungen

Jeder Service kann unabhängig deployed werden und in seiner eigenen Technologie geschrieben sein!

Schritt 1: Spring Cloud Dependencies

Erstelle ein neues Maven-Projekt. Füge hinzu:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2023.0.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <!-- Eureka Client für Service Discovery -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
    <!-- Feign für REST-Calls -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
</dependencies>

Schritt 2: Eureka Server (Service Discovery)

Was ist Eureka? Ein Service-Registry. Alle deine Microservices registrieren sich dort, damit sie sich gegenseitig finden können.

Erstelle ein neues Projekt eureka-server:

pom.xml:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

Application:

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

application.yml:

server:
  port: 8761

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

Starte den Server: Gehe zu http://localhost:8761 – du siehst das Eureka Dashboard!

Schritt 3: User-Service (Eureka Client)

Jetzt registrierst du deinen User-Service bei Eureka.

application.yml:

spring:
  application:
    name: user-service

server:
  port: 8081

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

Main Application:

@SpringBootApplication
@EnableEurekaClient
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

REST Controller:

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return new User(id, "Max Mustermann", "max@example.com");
    }
}

Starte beide Apps: Erst Eureka (8761), dann User-Service (8081). In der Eureka-Konsole siehst du deinen User-Service registriert!

Schritt 4: Order-Service mit Feign Client

Jetzt erstellen wir einen Order-Service, der den User-Service aufruft.

Main Application:

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

Feign Client Interface:

@FeignClient(name = "user-service")
public interface UserClient {
    
    @GetMapping("/api/users/{id}")
    User getUser(@PathVariable Long id);
}

Order Controller:

@RestController
@RequestMapping("/api/orders")
public class OrderController {
    
    private final UserClient userClient;
    
    @GetMapping("/{id}")
    public OrderDTO getOrder(@PathVariable Long id) {
        // Hole User vom User-Service
        User user = userClient.getUser(1L);
        
        // Baue Order
        return new OrderDTO(
            id, 
            "Order #" + id, 
            user.getUsername(),
            99.99
        );
    }
}

application.yml:

spring:
  application:
    name: order-service

server:
  port: 8082

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

Teste es: Rufe http://localhost:8082/api/orders/1 auf. Der Order-Service holt sich automatisch User-Daten vom User-Service!

Schritt 5: API Gateway

Ein API Gateway ist der zentrale Eintrittspunkt für alle deine Microservices.

Neues Projekt: gateway-service

pom.xml:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

application.yml:

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
        
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**

server:
  port: 8080

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

Jetzt kannst du alles über das Gateway aufrufen:

  • http://localhost:8080/api/users/1 → leitet zu User-Service weiter
  • http://localhost:8080/api/orders/1 → leitet zu Order-Service weiter

Schritt 6: Docker Compose

Erstelle docker-compose.yml:

version: '3.8'

services:
  eureka-server:
    build: ./eureka-server
    ports:
      - "8761:8761"
    networks:
      - microservices

  user-service:
    build: ./user-service
    depends_on:
      - eureka-server
    environment:
      - EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka/
    networks:
      - microservices

  order-service:
    build: ./order-service
    depends_on:
      - eureka-server
      - user-service
    environment:
      - EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka/
    networks:
      - microservices

  api-gateway:
    build: ./api-gateway
    depends_on:
      - eureka-server
    ports:
      - "8080:8080"
    environment:
      - EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka/
    networks:
      - microservices

networks:
  microservices:
    driver: bridge

Starte alles:

docker-compose up

🎓 Was du heute gelernt hast:

Template Engines:

✅ Alle wichtigen Template Engines für Spring Boot
✅ Mustache – Logic-less Templates
✅ JTE – High Performance, Type-safe
✅ Pebble – Twig für Java
✅ Groovy Templates – DSL Power
✅ Freemarker – Email & Reports
✅ JSP – Legacy (zu vermeiden)
✅ Entscheidungsmatrix – Welche wann?

Microservices:

✅ Service Discovery mit Eureka
✅ REST-Calls mit Feign
✅ API Gateway
✅ Docker Compose Setup


🏆 KURS ABGESCHLOSSEN! 🎉

Gratulation! Du hast 10 Tage Spring Boot Aufbau erfolgreich abgeschlossen!

Was du kannst:

  • ✅ REST APIs bauen
  • ✅ Template Engines vergleichen und auswählen
  • ✅ Microservices orchestrieren
  • ✅ Production-Ready Apps entwickeln

Du bist jetzt bereit für:


🚀 Was jetzt?

Das war Tag 20 des Spring Boot Kurses (Basic + Aufbau)!

Du kannst jetzt:

  • ✅ Spring Boot Anwendungen entwickeln
  • ✅ REST APIs bauen und absichern
  • ✅ Mit Datenbanken arbeiten (JPA)
  • ✅ Tests schreiben (JUnit, Mockito)
  • ✅ Security implementieren
  • ✅ Apps deployen (Docker Basics)
  • Einen realistischen Karriereplan erstellen

Deine nächsten 6 Monate:

  • 🎯 Portfolio aufbauen (Bountie + Open Source!)
  • 🎯 GitHub grün färben (Consistency!)
  • 🎯 Blog starten (zeig was du lernst!)
  • 🎯 Community beitreten (nicht alleine kämpfen!)
  • 🎯 Bewerben und JOB FINDEN! 💼

Das Team glaubt an dich!

Du hast die Skills aus 20 Tagen intensivem Learning. Jetzt zeig sie der Welt! 💪

Die Realität 2025:

  • Nach 20 Tagen Kurs alleine: 15-20% Job-Chance
  • Nach 20 Tagen Kurs + 6 Monate Portfolio: 70-80% Job-Chance

Welchen Weg wählst du? 🚀


Keep coding, keep learning, keep building! 🚀


Alle Blogbeiträge dieser Serie findest du hier: Spring Boot Aufbau – Komplette Übersicht


Tags: #SpringBoot #TemplateEngines #Mustache #JTE #Pebble #Groovy #Freemarker #Microservices #SpringCloud #Eureka #Feign #APIGateway #Tag10

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.