🎓 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.


🎨 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.