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

📍 Deine Position im Kurs
| Tag | Thema | Status |
|---|---|---|
| 1 | Auto-Configuration & Custom Starter | Voraussetzung |
| 2 | Spring Data JPA Basics | Voraussetzung |
| 3 | JPA Relationships & Queries | Voraussetzung |
| 4 | Spring Security Part 1 – Authentication | Voraussetzung |
| 5 | Spring Security Part 2 – Authorization | Voraussetzung |
| 6 | Spring Boot Caching & JSON | Voraussetzung |
| 7 | Messaging & Email | Voraussetzung |
| 8 | Testing & Dokumentation | Voraussetzung |
| 9 | Spring Boot Actuator | Voraussetzung |
| →10 | Template 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!
🎨 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
| Technologie | Rendering | Performance | Startzeit | SEO | Syntax | Spring Integration | Community | Use Case |
|---|---|---|---|---|---|---|---|---|
| Thymeleaf | Server-Side | Mittel | ⚡⚡⚡ Schnell | ⭐⭐⭐⭐⭐ Exzellent | HTML-ähnlich | ⭐⭐⭐⭐⭐ Native | Sehr groß | Standard für Spring Boot Web-Apps |
| Mustache | Server-Side | Hoch | ⚡⚡⚡ Schnell | ⭐⭐⭐⭐⭐ Exzellent | Minimalistisch | ⭐⭐⭐⭐ Starter | Groß (Multi-Language) | Einfache Templates, Multi-Language |
| JTE | Server-Side | Sehr hoch | ⚡⚡⚡ Schnell | ⭐⭐⭐⭐⭐ Exzellent | Java-ähnlich | ⭐⭐⭐ Manual | Klein aber aktiv | Performance-kritische Apps |
| Pebble | Server-Side | Hoch | ⚡⚡⚡ Schnell | ⭐⭐⭐⭐⭐ Exzellent | Twig-ähnlich | ⭐⭐⭐⭐ Starter | Mittel | PHP/Twig-Entwickler |
| Groovy | Server-Side | Mittel | ⚡⚡⚡ Schnell | ⭐⭐⭐⭐⭐ Exzellent | Groovy DSL | ⭐⭐⭐⭐ Starter | Mittel | Groovy-Projekte |
| Freemarker | Server-Side | Mittel | ⚡⚡⚡ Schnell | ⭐⭐⭐⭐⭐ Exzellent | FTL-Syntax | ⭐⭐⭐⭐ Starter | Groß | Email, Reports, Legacy |
| JSP | Server-Side | Niedrig | ⚡⚡ Langsam | ⭐⭐⭐⭐ Gut | Java + HTML | ⭐⭐⭐ Built-in | Sehr groß (Legacy) | Alte Java EE Apps |
| React | Client-Side | Sehr hoch (nach Load) | ⚡ Sehr langsam | ⭐⭐ Schwierig (SSR nötig) | JSX | ❌ Separate App | Sehr groß | SPAs, hochinteraktive UIs |
| Vue.js | Client-Side / Hybrid | Hoch (nach Load) | ⚡ Langsam | ⭐⭐⭐ Mittel (SSR möglich) | HTML-ähnlich | ❌ Separate App | Groß | SPAs, Progressive Web Apps |
| Angular | Client-Side | Hoch (nach Load) | ⚡ Sehr langsam | ⭐⭐ Schwierig (SSR nötig) | TypeScript + HTML | ❌ Separate App | Sehr 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):
| Technologie | Time to First Paint | Time to Interactive | Bundle Size |
|---|---|---|---|
| Thymeleaf | ~100ms | ~150ms | 0 KB (kein JS) |
| Mustache | ~80ms | ~120ms | 0 KB (kein JS) |
| JTE | ~50ms | ~90ms | 0 KB (kein JS) |
| React (CSR) | ~2000ms | ~3500ms | 150-300 KB |
| Vue.js (CSR) | ~1500ms | ~2800ms | 80-150 KB |
| Angular (CSR) | ~2500ms | ~4000ms | 200-400 KB |
| React + SSR (Next.js) | ~400ms | ~2000ms | 150-300 KB |
| Vue.js + SSR (Nuxt.js) | ~350ms | ~1800ms | 80-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:
| Syntax | Bedeutung | Beispiel |
|---|---|---|
{{variable}} | Variable ausgeben | {{user.name}} |
{{#section}}...{{/section}} | Section (Loop/Conditional) | {{#users}}...{{/users}} |
{{^section}}...{{/section}} | Inverted Section (else) | {{^error}}Kein Fehler{{/error}} |
{{.}} | Current item in loop | In {{#items}}{{.}}{{/items}} |
{{> partial}} | Include partial | {{> header}} |
{{! comment }} | Kommentar | {{! Dies ist ein Kommentar }} |
Partials (Fragments): templates/footer.mustache
<footer>
<p>© 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:
| Syntax | Bedeutung | Beispiel |
|---|---|---|
@import | Java-Import | @import de.example.User |
@param | Parameter-Deklaration (Type-safe!) | @param User user |
${expression} | Variable ausgeben (escaped) | ${user.name()} |
$unsafe{expression} | Unescaped HTML | $unsafe{richText} |
@if()...@endif | Conditional | @if(user.isActive())...@endif |
@for()...@endfor | Loop | @for(var item : items)...@endfor |
@template.name() | Template aufrufen | @template.layout.main() |
@…„ | Content Block | Fü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>
© 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 %}
© 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:
| Syntax | Bedeutung | Beispiel |
|---|---|---|
{{ 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:
| Filter | Beschreibung | Beispiel |
|---|---|---|
upper | Uppercase | {{ name | upper }} → „MAX“ |
lower | Lowercase | {{ name | lower }} → „max“ |
capitalize | Erster Buchstabe groß | {{ name | capitalize }} |
length | Länge | {{ items | length }} → 5 |
default | Default-Wert | {{ name | default("Guest") }} |
date | Datum formatieren | {{ now | date("dd.MM.yyyy") }} |
join | Array joinen | {{ items | join(", ") }} |
slice | Array 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:
| Syntax | Bedeutung | Beispiel |
|---|---|---|
${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:
| Function | Beschreibung | Beispiel |
|---|---|---|
?upper_case | Uppercase | ${name?upper_case} |
?lower_case | Lowercase | ${name?lower_case} |
?capitalize | Capitalize | ${name?capitalize} |
?size | Größe/Länge | ${items?size} |
?default("fallback") | Default-Wert | ${name?default("Guest")} |
?date | Datum formatieren | ${now?date("dd.MM.yyyy")} |
?join(", ") | Array joinen | ${items?join(", ")} |
?has_content | Prü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 Case | Empfehlung | Grund |
|---|---|---|
| Blog / Content-Site | Thymeleaf / JTE | SEO kritisch, statisch |
| E-Commerce Shop | Thymeleaf + Vue Widgets | SEO + etwas Interaktivität |
| Admin-Panel (intern) | Thymeleaf oder Vue.js | Schnell gebaut, SEO egal |
| Dashboard (Charts) | React oder Vue.js | Hochinteraktiv, Echtzeit |
| CRM-System | Angular | Enterprise, komplex |
| Social Media App | React | Hochinteraktiv, Mobile später |
| Email-Client (Gmail-like) | React | Maximale Interaktivität |
| Banking-App | Angular | Security, Enterprise |
| Startup MVP | Thymeleaf oder Vue.js | Schnell, flexibel |
| API-First Architektur | React / Vue / Angular | Backend = API only |
💡 Elyndra’s Hybrid-Empfehlung
Mein Favorit für die meisten Projekte:
Thymeleaf + Vue.js Widgets = Best of Both Worlds!
Warum?
- SEO funktioniert – Server-Side Rendering
- Schneller Start – Kein großes JS-Bundle
- Interaktivität wo nötig – Vue.js für einzelne Komponenten
- Ein Projekt – Nicht zwei separate Codebases
- Einfach zu deployen – Ein JAR/WAR
- 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:
- Thymeleaf – Pure Server-Side
- Thymeleaf + Vue.js Widget – Hybrid
- React SPA – Pure Client-Side
- 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 Case | Empfohlene Engine | Grund |
|---|---|---|
| Standard Spring Boot Web-App | Thymeleaf | Beste Integration, große Community |
| High-Performance App | JTE | Kompiliert, sehr schnell |
| Email-System | Freemarker / Mustache | Bewährt für Emails |
| Multi-Language Templates | Mustache | Funktioniert in Java, JS, Ruby, etc. |
| PHP/Twig Background | Pebble | Vertraute Syntax |
| Groovy-Projekt | Groovy Templates | Native Integration |
| Maximale Einfachheit | Mustache | Logic-less, minimalistisch |
| Complex Enterprise UI | Thymeleaf | Umfangreichste Features |
| Legacy-Migration | JSP → Thymeleaf | Moderner Stack |
| Admin-Panel intern | Thymeleaf / Pebble | Schnell, einfach |
| API-First + minimale UI | Mustache | Leichtgewichtig |
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:
- Mustache – Logic-less
- JTE – High-Performance
- 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 weiterhttp://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:
- Junior-Stellen als Spring Boot Developer
- Eigene Projekte
- Open-Source Contributions
- baue dein Portfolio aus
🚀 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

