Java Web Basic – Tag 5 von 10
Von Elyndra Valen, Senior Developer bei Java Fleet Systems Consulting
🗺️ Deine Position im Kurs
| Tag | Thema | Status |
|---|---|---|
| 1 | Java EE Überblick & HTTP | ✅ Abgeschlossen |
| 2 | HTTP-Protokoll Vertiefung & Zustandslosigkeit | ✅ Abgeschlossen |
| 3 | Servlets & Servlet API | ✅ Abgeschlossen |
| 4 | Deployment Descriptor & MVC vs Model 2 | ✅ Abgeschlossen |
| → 5 | JSP & Expression Languages | 👉 DU BIST HIER! |
| 6 | Java Beans, Actions, Scopes & Direktiven | 🔜 Kommt als nächstes |
| 7 | Include-Action vs Include-Direktive | 🔒 Noch nicht freigeschaltet |
| 8 | JSTL – Java Standard Tag Libraries | 🔒 Noch nicht freigeschaltet |
| 9 | Java Web und Datenbanken – Datasource | 🔒 Noch nicht freigeschaltet |
| 10 | Connection Pools & JDBC in Web-Umgebungen | 🔒 Noch nicht freigeschaltet |
Modul: Java Web Basic
Gesamt-Dauer: 10 Arbeitstage (je 8 Stunden)
Dauer heute: 8 Stunden
Dein Ziel: JSPs meistern und Expression Language verstehen
📋 Voraussetzungen für diesen Tag
Du brauchst:
- ✅ JDK 21 LTS installiert
- ✅ Apache NetBeans 22 (oder neuer)
- ✅ Payara Server 6.x konfiguriert
- ✅ Tag 1-4 abgeschlossen
- ✅ MVC/Model 2 verstanden
- ✅ Servlet-Grundlagen sitzen
Tag verpasst?
Spring zurück zu Tag 4, um MVC zu verstehen. JSPs sind die View in MVC!
Setup-Probleme?
Schreib uns: support@java-developer.online
⚡ Das Wichtigste in 30 Sekunden
Heute lernst du:
- ✅ Was JSPs sind und wie sie funktionieren
- ✅ JSP-Lifecycle verstehen (Translation → Compilation → Execution)
- ✅ Expression Language (EL) Syntax
- ✅ Implizite Objekte in JSPs
- ✅ JSP-Direktiven (
<%@ page %>,<%@ taglib %>,<%@ include %>) - ✅ Scriptlets vs. Expression Language (und warum EL besser ist!)
Am Ende des Tages kannst du:
- JSPs schreiben und verstehen
- Expression Language verwenden
- Implizite Objekte nutzen
- Direktiven richtig einsetzen
- Scriptlets vermeiden (Best Practice!)
Zeit-Investment: ~8 Stunden
Schwierigkeitsgrad: Mittel (neue Syntax!)
👋 Willkommen zu Tag 5!
Hi! 👋
Elyndra hier. Heute wird’s spannend – wir lernen JavaServer Pages !
Kurzwiederholung: Challenge von Tag 4
Gestern solltest du eine einfache Blog-Anwendung mit Model 2 Architektur erstellen. Falls du es noch nicht gemacht hast, hier die Musterlösung:
Model: BlogPost.java
package com.blog.model;
import java.time.LocalDate;
public class BlogPost {
private int id;
private String title;
private String content;
private String author;
private LocalDate date;
public BlogPost(int id, String title, String content,
String author, LocalDate date) {
this.id = id;
this.title = title;
this.content = content;
this.author = author;
this.date = date;
}
// Getters & Setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public LocalDate getDate() { return date; }
public void setDate(LocalDate date) { this.date = date; }
}
Model: BlogPostDAO.java
package com.blog.model;
import java.time.LocalDate;
import java.util.*;
public class BlogPostDAO {
// In-Memory-Daten (normalerweise aus Database)
private static List<BlogPost> posts = Arrays.asList(
new BlogPost(1, "Java Web Basics",
"Learn Servlets and JSP...",
"Elyndra", LocalDate.of(2025, 10, 1)),
new BlogPost(2, "MVC Pattern",
"Understanding Model 2...",
"Elyndra", LocalDate.of(2025, 10, 15)),
new BlogPost(3, "Deployment Descriptor",
"web.xml explained...",
"Elyndra", LocalDate.of(2025, 10, 20))
);
public List<BlogPost> getAll() {
return posts;
}
public BlogPost getById(int id) {
return posts.stream()
.filter(post -> post.getId() == id)
.findFirst()
.orElse(null);
}
}
Controller: BlogServlet.java
package com.blog.controller;
import com.blog.model.BlogPost;
import com.blog.model.BlogPostDAO;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.*;
import java.io.IOException;
import java.util.List;
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private BlogPostDAO blogDAO = new BlogPostDAO();
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String idParam = request.getParameter("id");
if (idParam == null) {
// Alle Posts anzeigen
List<BlogPost> posts = blogDAO.getAll();
request.setAttribute("posts", posts);
request.getRequestDispatcher("/WEB-INF/views/blog-list.jsp")
.forward(request, response);
} else {
// Ein Post anzeigen
try {
int id = Integer.parseInt(idParam);
BlogPost post = blogDAO.getById(id);
if (post == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
"Blog post not found");
return;
}
request.setAttribute("post", post);
request.getRequestDispatcher("/WEB-INF/views/blog-post.jsp")
.forward(request, response);
} catch (NumberFormatException e) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST,
"Invalid post ID");
}
}
}
}
View: WEB-INF/views/blog-list.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<!DOCTYPE html>
<html>
<head>
<title>Blog Posts</title>
</head>
<body>
<h1>All Blog Posts</h1>
<ul>
<c:forEach var="post" items="${posts}">
<li>
<h2>
<a href="blog?id=${post.id}">${post.title}</a>
</h2>
<p>By ${post.author} on ${post.date}</p>
</li>
</c:forEach>
</ul>
</body>
</html>
View: WEB-INF/views/blog-post.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<title>${post.title}</title>
</head>
<body>
<h1>${post.title}</h1>
<p><em>By ${post.author} on ${post.date}</em></p>
<div>
${post.content}
</div>
<p><a href="blog">← Back to all posts</a></p>
</body>
</html>
Super gemacht! 🎉 Das ist saubere Model 2 Architektur!
Erinnerst du dich an Tag 4?
Wir haben gelernt, dass in Model 2:
- Controller (Servlet) empfängt Requests und steuert
- Model (Java) enthält Business-Logik und Daten
- View (JSP) zeigt Daten an
Heute fokussieren wir uns auf die View – JSPs!
Was dich heute erwartet:
In den letzten Tagen hast du Servlets geschrieben, die HTML erzeugen:
out.println("<html>");
out.println("<body>");
out.println("<h1>Hello, " + name + "</h1>");
out.println("</body>");
out.println("</html>");
Das funktioniert – ist aber mühsam und fehleranfällig!
Mit JSPs kannst du HTML schreiben wie gewohnt, und Java-Code dort einbetten, wo du ihn brauchst:
<html>
<body>
<h1>Hello, ${name}!</h1>
</body>
</html>
Viel einfacher, oder?
Keine Sorge:
JSPs sehen am Anfang wie „Magie“ aus. Aber ich erkläre dir genau, was unter der Haube passiert. Nach heute wirst du verstehen, warum JSPs so mächtig sind – und wie du sie richtig einsetzt.
Los geht’s! 🎨
🟢 GRUNDLAGEN: Was sind JSPs?
Definition
JSP (JavaServer Pages) ist eine Technologie, die es erlaubt, dynamische Webseiten zu erstellen, indem man Java-Code in HTML einbettet.
Wichtig zu verstehen:
JSPs sind KEINE eigene Sprache! JSPs sind eine Templating-Technologie für Java Web.
Unter der Haube werden JSPs zu Servlets kompiliert!
Das große Bild: Von HTML zu JSP
Evolution der Webseiten-Erzeugung in Java:
Stufe 1: Statisches HTML
<!-- Datei: hello.html -->
<html>
<body>
<h1>Hello, World!</h1>
</body>
</html>
Problem: Keine Dynamik! Immer gleicher Content.
Stufe 2: Servlet mit HTML-Output
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String name = request.getParameter("name");
out.println("<html>");
out.println("<head><title>Hello</title></head>");
out.println("<body>");
out.println("<h1>Hello, " + name + "!</h1>");
out.println("<form>");
out.println("<input type='text' name='name'>");
out.println("<button>Submit</button>");
out.println("</form>");
out.println("</body>");
out.println("</html>");
}
}
Vorteile: ✅ Dynamisch – Name kommt aus Request
Nachteile: ❌ HTML in Java-Strings (unlesbar!)
❌ Kein Syntax-Highlighting für HTML
❌ Designer können nicht mitarbeiten
❌ Jede Änderung = Recompile
❌ Fehleranfällig (vergessene Quotes, Tags)
Stufe 3: JSP (Die richtige Lösung)
<!-- Datei: hello.jsp -->
<html>
<head><title>Hello</title></head>
<body>
<h1>Hello, ${param.name}!</h1>
<form>
<input type="text" name="name">
<button>Submit</button>
</form>
</body>
</html>
Vorteile: ✅ Dynamisch
✅ HTML ist HTML (lesbar!)
✅ Syntax-Highlighting funktioniert
✅ Designer können mitarbeiten
✅ Änderungen = kein Recompile (in DEV)
✅ Sauber und wartbar
Das ist der Weg!
Wie JSPs funktionieren – Der Lifecycle
Wichtig zu verstehen: JSPs sind NICHT interpretiert! Sie werden zu Servlets kompiliert.
Der JSP-Lifecycle in 3 Schritten:
1. TRANSLATION 2. COMPILATION 3. EXECUTION (JSP → Java) (Java → .class) (Servlet läuft) hello.jsp → hello_jsp.java → hello_jsp.class → Response
Schritt 1: Translation (JSP → Java)
Deine JSP:
<!-- hello.jsp -->
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<body>
<h1>Hello, ${param.name}!</h1>
</body>
</html>
Wird übersetzt zu (vereinfacht):
// hello_jsp.java (automatisch generiert!)
public class hello_jsp extends HttpServlet {
@Override
public void _jspService(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
JspWriter out = response.getWriter();
out.write("<html>\n");
out.write("<body>\n");
out.write(" <h1>Hello, ");
out.write(request.getParameter("name"));
out.write("!</h1>\n");
out.write("</body>\n");
out.write("</html>\n");
}
}
Was ist passiert?
- JSP wurde zu einer Servlet-Klasse konvertiert
- HTML wurde zu
out.write()Statements ${param.name}wurde zurequest.getParameter("name")
Wo liegt diese generierte Datei?
In Payara:
C:\payara6\glassfish\domains\domain1\generated\jsp\
Jede JSP hat dort eine .java Datei!
Schritt 2: Compilation (Java → Bytecode)
Die generierte .java Datei wird zu .class kompiliert – wie jede normale Java-Datei.
Ergebnis:
hello_jsp.class
Diese Klasse ist ein vollwertiges Servlet!
Schritt 3: Execution (Servlet läuft)
Wenn ein Request kommt:
- Webcontainer lädt
hello_jsp.class - Ruft
_jspService()Methode auf - HTML wird generiert und zurückgeschickt
Genau wie bei einem normalen Servlet!
Wann passiert Translation & Compilation?
Zwei Modi:
1. On-Demand (Standard in DEV)
- JSP wird beim ersten Zugriff übersetzt und kompiliert
- Vorteil: Änderungen an JSP sind sofort sichtbar (Reload genügt)
- Nachteil: Erster Request ist langsam
2. Pre-Compilation (PROD)
- JSPs werden beim Deployment übersetzt und kompiliert
- Vorteil: Schneller, keine Verzögerung
- Nachteil: Jede Änderung = Neues Deployment
In Entwicklung:
Du änderst die JSP, drückst F5 im Browser – der Container merkt die Änderung, recompiled automatisch, und du siehst die neue Version!
In Production:
JSPs werden pre-compiled. Keine automatische Recompilation!
Implizite Objekte in JSPs
Was sind implizite Objekte?
In JSPs stehen dir automatisch bestimmte Objekte zur Verfügung – ohne dass du sie deklarieren musst.
Die wichtigsten impliziten Objekte:
| Objekt | Typ | Beschreibung |
|---|---|---|
request | HttpServletRequest | Der aktuelle Request |
response | HttpServletResponse | Die aktuelle Response |
session | HttpSession | Die aktuelle Session |
application | ServletContext | Der Application-Kontext |
out | JspWriter | Output-Stream |
pageContext | PageContext | Zugriff auf alle Scopes |
page | Object | Die JSP selbst (this) |
config | ServletConfig | Servlet-Konfiguration |
exception | Throwable | Exception (nur in Error-Pages) |
Beispiel (Scriptlet – bald lernen wir die bessere Alternative!):
<%
String name = request.getParameter("name");
session.setAttribute("lastVisit", new Date());
out.println("Hello, " + name);
%>
Du musst NICHT schreiben:
HttpServletRequest request = ...; // NICHT nötig!
Es ist automatisch da!
🟡 PROFESSIONALS: JSP-Syntax im Detail
Die verschiedenen JSP-Elemente
JSPs haben verschiedene Syntax-Elemente:
- Direktiven – Konfiguration
- Declarations – Variablen & Methoden deklarieren (VERALTET!)
- Scriptlets – Java-Code (VERALTET!)
- Expressions – Werte ausgeben (VERALTET!)
- Expression Language (EL) – Moderne Alternative (DAS IST DER WEG! ✅)
- Actions – Spezielle Tags
Schauen wir uns jedes Element an:
1. Direktiven
Syntax: <%@ direktive attribut="wert" %>
Direktiven sind Anweisungen an den JSP-Container.
Page-Direktive
Die wichtigste Direktive!
<%@ page contentType="text/html;charset=UTF-8"
language="java"
session="true"
errorPage="error.jsp"
isErrorPage="false"
import="java.util.*, java.time.*" %>
Attribute im Detail:
contentType – MIME-Type der Response
<%@ page contentType="text/html;charset=UTF-8" %>
Setzt den Content-Type Header:
Content-Type: text/html;charset=UTF-8
Andere Content-Types:
<%@ page contentType="application/json" %> <%@ page contentType="text/plain" %> <%@ page contentType="application/xml" %>
language – Programmiersprache (immer java)
<%@ page language="java" %>
Theoretisch könnte man andere Sprachen unterstützen – praktisch gibt’s nur Java.
session – Session automatisch erstellen?
<%@ page session="true" %> <!-- Default --> <%@ page session="false" %> <!-- Keine Session! -->
session="true" (Default):
- JSP erstellt automatisch eine Session
sessionObjekt ist verfügbar
session="false":
- Keine Session-Objekt
- Verwendet für statische Pages oder APIs
errorPage – Wohin bei Exception?
<%@ page errorPage="/error.jsp" %>
Wenn eine Exception auftritt, wird der User automatisch zu error.jsp weitergeleitet.
error.jsp:
<%@ page isErrorPage="true" %>
<html>
<body>
<h1>Error occurred!</h1>
<p>${exception.message}</p>
</body>
</html>
isErrorPage="true" macht exception Objekt verfügbar!
import – Java-Klassen importieren
<%@ page import="java.util.List" %> <%@ page import="java.util.ArrayList" %> <%@ page import="com.shop.model.Product" %>
Oder alles zusammen:
<%@ page import="java.util.*, java.time.*, com.shop.model.*" %>
buffer – Output-Buffer-Größe
<%@ page buffer="8kb" %> <!-- 8 Kilobyte --> <%@ page buffer="none" %> <!-- Kein Buffer -->
Buffer bedeutet: Output wird gesammelt, bevor er gesendet wird.
Warum wichtig?
Solange Buffer nicht voll ist, kannst du noch Redirects machen oder Headers setzen!
Include-Direktive
<%@ include file="header.jsp" %> <h1>Main Content</h1> <%@ include file="footer.jsp" %>
Was passiert?
Die Dateien werden zur Translation-Zeit eingefügt – wie Copy/Paste!
Unterschied zu <jsp:include>:
<%@ include %>– Compile-Time (statisch, schneller)<jsp:include>– Runtime (dynamisch, flexibler)
Mehr dazu an Tag 7!
Taglib-Direktive
<%@ taglib prefix="c" uri="jakarta.tags.core" %> <%@ taglib prefix="fmt" uri="jakarta.tags.fmt" %>
Importiert Tag-Libraries (JSTL).
Mehr dazu an Tag 8!
2. Declarations (VERALTET – nicht verwenden!)
Syntax: <%! ... %>
Deklariert Variablen und Methoden auf Klassen-Ebene.
<%!
private int counter = 0;
public int getNextNumber() {
return ++counter;
}
%>
<p>Request number: <%= getNextNumber() %></p>
Problem:
counter ist eine Instanzvariable des Servlets. Wird von ALLEN Requests geteilt!
Race-Condition!
Zwei gleichzeitige Requests → counter wird durcheinander gebracht!
Lösung:
Verwende NIE Declarations! Nutze Servlets für solche Logik.
3. Scriptlets (VERALTET – nicht verwenden!)
Syntax: <% ... %>
Java-Code direkt in der JSP.
<%
String name = request.getParameter("name");
if (name == null || name.isEmpty()) {
name = "Guest";
}
%>
<h1>Hello, <%= name %>!</h1>
<%
for (int i = 0; i < 5; i++) {
%>
<p>Item <%= i %></p>
<%
}
%>
Warum ist das schlecht?
❌ Java-Code und HTML gemischt (unlesbar!)
❌ Schwer zu warten
❌ Schwer zu testen
❌ Designer können nicht damit arbeiten
❌ Kein automatisches HTML-Escaping (XSS-Gefahr!)
Das ist Spaghetti-Code – wie Model 1!
4. Expressions (VERALTET – nicht verwenden!)
Syntax: <%= ... %>
Gibt den Wert eines Java-Ausdrucks aus.
<p>Name: <%= request.getParameter("name") %></p>
<p>Session ID: <%= session.getId() %></p>
<p>Current Time: <%= new java.util.Date() %></p>
Wird übersetzt zu:
out.print(request.getParameter("name"));
out.print(session.getId());
out.print(new java.util.Date());
Problem:
Wie Scriptlets – Java-Code in HTML!
5. Expression Language (EL) – Die moderne Lösung! ✅
Syntax: ${ ... }
Die richtige Art, Werte in JSPs auszugeben!
<p>Name: ${param.name}</p>
<p>Session ID: ${pageContext.session.id}</p>
<p>Product Name: ${product.name}</p>
Warum ist EL besser?
✅ Kürzer und lesbarer
✅ Automatisches HTML-Escaping (XSS-Schutz!)
✅ Null-Safe (kein NullPointerException!)
✅ Designer-freundlich
✅ Kein Java-Code sichtbar
EL ist der Standard ab JSP 2.0! Nutze es!
🟢 GRUNDLAGEN: Expression Language (EL)
EL-Syntax Grundlagen
Zugriff auf Attribute:
${attributeName}
Sucht in dieser Reihenfolge:
- Page-Scope
- Request-Scope
- Session-Scope
- Application-Scope
Beispiel:
// Im Servlet:
request.setAttribute("user", userObject);
<!-- In JSP: -->
<p>Welcome, ${user.name}!</p>
EL ist null-safe!
Wenn user null ist, gibt EL einfach einen leeren String zurück – KEIN NullPointerException!
Implizite EL-Objekte
EL hat eigene implizite Objekte:
| EL-Objekt | Beschreibung | Beispiel |
|---|---|---|
param | Request-Parameter (einzelner Wert) | ${param.name} |
paramValues | Request-Parameter (mehrere Werte) | ${paramValues.hobby[0]} |
header | HTTP-Header | ${header['User-Agent']} |
headerValues | HTTP-Header (mehrere Werte) | ${headerValues.Accept} |
cookie | Cookies | ${cookie.sessionId.value} |
initParam | Context-Parameter | ${initParam['db.url']} |
pageContext | PageContext-Objekt | ${pageContext.request.contextPath} |
pageScope | Page-Scope Attributes | ${pageScope.localVar} |
requestScope | Request-Scope Attributes | ${requestScope.product} |
sessionScope | Session-Scope Attributes | ${sessionScope.user} |
applicationScope | Application-Scope Attributes | ${applicationScope.config} |
Beispiele:
<!-- Request-Parameter -->
URL: /page?name=Anna&age=25
<p>Name: ${param.name}</p> <!-- Anna -->
<p>Age: ${param.age}</p> <!-- 25 -->
<!-- Context-Path (wichtig für Links!) -->
<a href="${pageContext.request.contextPath}/products">Products</a>
<!-- Session-Attribute -->
<p>Logged in as: ${sessionScope.user.name}</p>
<!-- Cookie-Werte -->
<p>Session: ${cookie.JSESSIONID.value}</p>
<!-- Header -->
<p>Your Browser: ${header['User-Agent']}</p>
EL-Operatoren
Arithmetische Operatoren:
${5 + 3} <!-- 8 -->
${5 - 3} <!-- 2 -->
${5 * 3} <!-- 15 -->
${5 / 3} <!-- 1.6666... -->
${5 div 3} <!-- 1.6666... (gleich wie /) -->
${5 % 3} <!-- 2 -->
${5 mod 3} <!-- 2 (gleich wie %) -->
Vergleichs-Operatoren:
${5 == 3} <!-- false -->
${5 eq 3} <!-- false (gleich wie ==) -->
${5 != 3} <!-- true -->
${5 ne 3} <!-- true (gleich wie !=) -->
${5 < 3} <!-- false -->
${5 lt 3} <!-- false (gleich wie <) -->
${5 > 3} <!-- true -->
${5 gt 3} <!-- true (gleich wie >) -->
${5 <= 3} <!-- false -->
${5 le 3} <!-- false (gleich wie <=) -->
${5 >= 3} <!-- true -->
${5 ge 3} <!-- true (gleich wie >=) -->
Warum eq, lt, gt, etc.?
Weil < und > in XML/HTML Sonderzeichen sind! lt und gt sind „less than“ und „greater than“.
Logische Operatoren:
${true && false} <!-- false -->
${true and false} <!-- false (gleich wie &&) -->
${true || false} <!-- true -->
${true or false} <!-- true (gleich wie ||) -->
${!true} <!-- false -->
${not true} <!-- false (gleich wie !) -->
Conditional (Ternary) Operator:
${user != null ? user.name : 'Guest'}
${product.stock > 0 ? 'In Stock' : 'Out of Stock'}
${age >= 18 ? 'Adult' : 'Minor'}
Syntax: ${condition ? ifTrue : ifFalse}
Empty Operator:
${empty param.name} <!-- true wenn name null oder "" -->
${not empty user} <!-- true wenn user existiert -->
${empty cart.items} <!-- true wenn Liste leer -->
empty prüft:
- Strings: null oder
"" - Collections: null oder leer
- Arrays: null oder length 0
Property-Zugriff in EL
Dot-Notation:
${user.name}
${product.price}
${order.customer.address.street}
Entspricht:
user.getName() product.getPrice() order.getCustomer().getAddress().getStreet()
EL ruft automatisch die Getter-Methoden auf!
Bracket-Notation:
${user['name']}
${product['price']}
Gleich wie Dot-Notation – aber nützlich bei:
- Sonderzeichen in Namen:
${header['User-Agent']} <!-- "User-Agent" hat "-" -->
- Dynamische Property-Namen:
${user[propertyName]} <!-- propertyName ist Variable -->
Listen und Arrays in EL
Listen:
// Im Servlet:
List<Product> products = productDAO.getAll();
request.setAttribute("products", products);
<!-- In JSP: -->
${products[0].name} <!-- Erstes Produkt -->
${products[1].price} <!-- Zweites Produkt -->
${products.size()} <!-- Anzahl Produkte -->
Arrays:
// Im Servlet:
String[] colors = {"Red", "Green", "Blue"};
request.setAttribute("colors", colors);
<!-- In JSP: -->
${colors[0]} <!-- Red -->
${colors[1]} <!-- Green -->
${colors.length} <!-- 3 -->
🟡 PROFESSIONALS: EL Advanced Features
Null-Safety in EL
Das Beste an EL: Null-Safety!
<!-- Angenommen, user ist null -->
<!-- Scriptlet (SCHLECHT - crasht!): -->
<%= user.getName() %> <!-- NullPointerException! -->
<!-- EL (GUT - sicher!): -->
${user.name} <!-- Zeigt nichts (leerer String) -->
EL gibt bei null einfach einen leeren String zurück!
Besser lesbar als:
<% if (user != null && user.getName() != null) { %>
<%= user.getName() %>
<% } %>
vs.
${user.name}
EL-Funktionen
Statische Methoden aufrufen:
<%@ taglib prefix="fn" uri="jakarta.tags.functions" %>
<!-- String-Länge -->
${fn:length(user.name)}
<!-- String uppercase -->
${fn:toUpperCase(user.name)}
<!-- Substring -->
${fn:substring(user.name, 0, 5)}
<!-- Enthält? -->
${fn:contains(user.email, '@gmail.com')}
Mehr dazu an Tag 8 (JSTL)!
Collection-Operationen
Mit JSTL forEach:
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<ul>
<c:forEach var="product" items="${products}">
<li>${product.name} - $${product.price}</li>
</c:forEach>
</ul>
Mit EL und JSTL if:
<c:if test="${product.stock > 0}">
<p>In Stock!</p>
</c:if>
<c:if test="${product.stock == 0}">
<p>Out of Stock!</p>
</c:if>
Best Practices für EL
✅ DO:
<!-- Verwende EL für alle Daten-Ausgaben -->
<h1>${product.name}</h1>
<p>${product.description}</p>
<!-- Verwende conditional für einfache Bedingungen -->
<p class="${product.stock > 0 ? 'in-stock' : 'out-of-stock'}">
${product.stock > 0 ? 'Available' : 'Sold Out'}
</p>
<!-- Verwende pageContext für Context-Path -->
<a href="${pageContext.request.contextPath}/products">Products</a>
❌ DON’T:
<!-- NICHT: Scriptlets verwenden -->
<% Product product = (Product) request.getAttribute("product"); %>
<h1><%= product.getName() %></h1>
<!-- NICHT: Komplexe Logik in EL -->
${product.stock > 10 && product.price < 100 && product.category == 'Electronics'
&& product.rating >= 4.0 ? 'Hot Deal!' : product.stock > 0 ? 'Available' : 'Out of Stock'}
<!-- NICHT: Business-Logik in View -->
${productService.calculateDiscountedPrice(product, user.membershipLevel)}
Regel:
- Einfache Darstellung? → EL
- Komplexe Logik? → Controller/Service!
🔵 BONUS: Fortgeschrittene JSP-Techniken
Custom EL-Funktionen erstellen
Eigene Funktionen definieren:
1. Java-Klasse mit statischer Methode:
package com.shop.utils;
public class StringUtils {
public static String capitalize(String text) {
if (text == null || text.isEmpty()) {
return text;
}
return text.substring(0, 1).toUpperCase() + text.substring(1);
}
}
2. TLD-Datei (Tag Library Descriptor):
<!-- WEB-INF/functions.tld -->
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-jsptaglibrary_3_0.xsd"
version="3.0">
<tlib-version>1.0</tlib-version>
<short-name>util</short-name>
<uri>http://shop.com/functions</uri>
<function>
<name>capitalize</name>
<function-class>com.shop.utils.StringUtils</function-class>
<function-signature>
java.lang.String capitalize(java.lang.String)
</function-signature>
</function>
</taglib>
3. In JSP verwenden:
<%@ taglib prefix="util" uri="http://shop.com/functions" %>
<p>${util:capitalize(product.name)}</p>
JSP-Fragments
Wiederverwendbare Snippets:
header.jspf (Fragment):
<%-- header.jspf --%>
<header>
<h1>My Shop</h1>
<nav>
<a href="${pageContext.request.contextPath}/">Home</a>
<a href="${pageContext.request.contextPath}/products">Products</a>
<a href="${pageContext.request.contextPath}/cart">Cart</a>
</nav>
</header>
Einbinden:
<%@ include file="/WEB-INF/fragments/header.jspf" %>
<main>
<!-- Page Content -->
</main>
<%@ include file="/WEB-INF/fragments/footer.jspf" %>
Dateiendung .jspf = JSP Fragment (Convention!)
💬 Real Talk: Scriptlets vs. EL im echten Leben
Java Fleet Büro, 16:00 Uhr. Nova starrt frustriert auf ihren Bildschirm, vor ihr eine JSP voller Scriptlets.
Nova: „Elyndra, ich verstehe nicht, warum Scriptlets so schlimm sein sollen. Ich kann damit doch alles machen! Ist viel flexibler als diese EL-Syntax…“
Elyndra (kommt mit Kaffeetasse): „Zeig mal her. Oh wow… das ist… impressive in der falschen Richtung.“
Nova: „Was? Es funktioniert doch!“
Cassian (schaut über Novas Schulter): „Hmm. 200 Zeilen JSP mit… ist das ein for-Loop in einem if-Statement in einem try-catch-Block… in einer JSP?“
Nova (defensiv): „Ja, und? Ich brauche die Logik dort!“
Elyndra: „Nova, real talk: Kannst du mir erklären, was Zeile 87 macht, ohne die ganze Datei zu lesen?“
Nova (scrollt, sucht): „Ähm… moment… das ist… warte…“
Cassian: „Genau das ist das Problem. Du hast das vor zwei Stunden geschrieben und findest dich schon nicht mehr zurecht.“
Kofi (kommt vorbei): „Dude, ich hab letzte Woche ein Legacy-Projekt übernommen. VOLLER Scriptlets. Ich hab drei Tage gebraucht, nur um zu verstehen, wo die Business-Logik ist. Spoiler: überall!“
Nova: „Okay okay, aber EL kann doch nicht ALLES, was Scriptlets können…“
Elyndra: „Das ist der Punkt! EL SOLL nicht alles können. Views sollen nur ZEIGEN, nicht BERECHNEN.“
Cassian: „Stell dir vor, du bist Designer. Du kannst HTML und CSS. Jetzt kommst du in eine JSP und siehst:“
<%
if (user != null && user.getRole().equals("admin")) {
List<Product> products = productDAO.getAll();
for (Product p : products) {
if (p.getStock() > 0) {
%>
<div><%= p.getName() %></div>
<%
}
}
}
%>
Cassian: „Wie soll ein Designer damit arbeiten?“
Nova: „Gar nicht, schätze ich…“
Elyndra: „Exactly. Jetzt schau dir das an:“
<c:if test="${not empty products}">
<c:forEach var="product" items="${products}">
<c:if test="${product.stock > 0}">
<div>${product.name}</div>
</c:if>
</c:forEach>
</c:if>
Elyndra: „Siehst du den Unterschied? Ein Designer kann das lesen. Es SIEHT aus wie HTML mit ein paar Extra-Tags.“
Nova: „Aber woher kommt products? Ich sehe keinen Java-Code!“
Cassian: „DAS ist der Punkt! Die Logik ist im Servlet – wo sie hingehört:“
List<Product> products = productDAO.getAll();
request.setAttribute("products", products);
request.getRequestDispatcher("/view.jsp").forward(request, response);
Kofi: „Separation of Concerns, yo. Servlet macht die Arbeit, JSP zeigt das Ergebnis. Clean!“
Nova: „Okay, ich geb’s zu… meine JSP sieht aus wie ein Unfall. Aber wie soll ich jetzt damit umgehen? Alles neu schreiben?“
Elyndra: „Nicht alles auf einmal. Regel: Neuer Code = nur EL. Alter Code = bei Gelegenheit refactoren. Baby steps!“
Cassian: „Und honestly: In einem Monat wirst du deine alten Scriptlet-JSPs angucken und dich fragen, warum du je gedacht hast, das sei eine gute Idee.“
Nova (seufzt): „Fine. Ich probier’s. Aber wenn ich stuck bin, hilfst du mir, Elyndra?“
Elyndra: „Always! Das ist ein Team hier. Und Nova? Welcome to the EL side. Wir haben keine Scriptlets.“
Nova (grinst): „Und hoffentlich auch keine NullPointerExceptions mehr…“
Kofi: „Amen to that!“
✅ Checkpoint: Hast du es verstanden?
Zeit für einen Reality-Check! Beantworte diese Fragen, um zu prüfen, ob du heute alles verstanden hast.
Quiz:
Frage 1: Erkläre den JSP-Lifecycle. Was sind die drei Schritte?
Frage 2: Was ist der Unterschied zwischen On-Demand-Compilation und Pre-Compilation?
Frage 3: Nenne 5 implizite Objekte in JSPs und erkläre, wofür sie verwendet werden.
Frage 4: Was ist der Unterschied zwischen <%@ include %> und <jsp:include>?
Frage 5: Warum solltest du Scriptlets vermeiden? Nenne mindestens 3 Gründe.
Frage 6: Was macht Expression Language (EL) null-safe? Warum ist das wichtig?
Frage 7: Erkläre den Unterschied zwischen ${param.name} und ${paramValues.hobby}.
Frage 8: Was ist der empty Operator in EL und wann würdest du ihn verwenden?
🎯 Mini-Challenge
Aufgabe: Erstelle eine JSP für eine Produkt-Detailseite mit Expression Language.
Requirements:
- Daten vom Controller:
Product product = productDAO.getById(id); request.setAttribute("product", product); - JSP soll anzeigen:
- Produkt-Name
- Beschreibung
- Preis (formatiert mit $)
- Lagerbestand
- „In Stock“ wenn > 0, sonst „Out of Stock“
- „Add to Cart“ Button (nur wenn auf Lager)
- Verwende:
- EL (kein Scriptlet!)
- Conditional (ternary) Operator
- JSTL
<c:if>für Button
- Bonus:
- Context-Path verwenden für Links
- CSS-Klasse dynamisch setzen (in-stock / out-of-stock)
Hinweise:
- Nutze
${product.name},${product.price}, etc. - Verwende
${product.stock > 0 ? 'In Stock' : 'Out of Stock'} - JSTL Taglib einbinden:
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
Lösung:
Die Lösung zu dieser Challenge findest du am Anfang von Tag 6 als Kurzwiederholung! 🚀
Alternativ kannst du die Musterlösung im GitHub-Projekt checken: https://github.com/java-fleet/java-web-basic-examples
Geschafft? 🎉
Dann bist du bereit für die FAQ-Sektion!
❓ Häufig gestellte Fragen
Frage 1: Werden JSPs wirklich zu Servlets kompiliert? Wo kann ich das sehen?
Ja, absolut! JSPs sind nur eine angenehmere Syntax für Servlets.
Wo findest du die generierten Dateien?
Payara Server:
C:\payara6\glassfish\domains\domain1\generated\jsp\
Tomcat:
C:\tomcat\work\Catalina\localhost\[app-name]\org\apache\jsp\
Schau mal rein! Öffne eine .java Datei dort. Du wirst sehen:
- Deine JSP wurde zu einer Servlet-Klasse
- HTML wurde zu
out.write()Statements - EL wurde zu Java-Code übersetzt
Warum ist das wichtig zu wissen?
Wenn du Fehler in deiner JSP hast, zeigt der Stack-Trace oft die GENERIERTE Java-Datei an, nicht deine originale JSP. Wenn du verstehst, dass JSPs kompiliert werden, kannst du diese Fehler besser debuggen!
Frage 2: EL wird nicht ausgewertet – ich sehe ${param.name} wörtlich im Browser. Was ist falsch?
Häufigste Ursache: web.xml ist zu alt!
Lösung 1: web.xml Version prüfen
<!-- FALSCH - zu alt! -->
<web-app version="2.3" ...>
<!-- RICHTIG - mindestens 2.4+ -->
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
EL wurde in Servlet 2.4 (JSP 2.0) eingeführt. Ältere Versionen kennen EL nicht!
Lösung 2: Explizit aktivieren
<%@ page isELIgnored="false" %>
Oder in web.xml:
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<el-ignored>false</el-ignored>
</jsp-property-group>
</jsp-config>
Frage 3: Was ist der Unterschied zwischen ${product.name} und ${product['name']}?
Funktional: Kein Unterschied! Beide rufen product.getName() auf.
Wann welche Notation?
Dot-Notation (${product.name}):
- ✅ Einfacher zu lesen
- ✅ Standard-Syntax
- ❌ Funktioniert nicht bei Sonderzeichen im Property-Namen
Bracket-Notation (${product['name']}):
- ✅ Funktioniert immer
- ✅ Nötig bei Sonderzeichen (z.B.
${header['User-Agent']}) - ✅ Ermöglicht dynamische Property-Namen (
${product[propertyName]})
Best Practice: Verwende Dot-Notation, außer du hast einen Grund für Brackets!
Frage 4: Kann ich mit EL Methoden aufrufen?
Ja, aber nur Getter!
<!-- ✅ GUT: -->
${product.name} <!-- ruft getName() -->
${product.price} <!-- ruft getPrice() -->
${products.size()} <!-- ruft size() -->
${user.admin} <!-- ruft isAdmin() oder getAdmin() -->
<!-- ❌ SCHLECHT: -->
${productService.calculateDiscount(product)} <!-- Geht nicht direkt! -->
Für komplexere Methoden:
Option 1: Verwende JSTL Functions (Tag 8!)
<%@ taglib prefix="fn" uri="jakarta.tags.functions" %>
${fn:length(product.name)}
${fn:toUpperCase(product.name)}
Option 2: Bereite Daten im Servlet vor!
// Servlet:
double discount = productService.calculateDiscount(product);
request.setAttribute("discount", discount);
<!-- JSP: -->
<p>Discount: ${discount}%</p>
Best Practice: Business-Logik gehört NICHT in die View!
Frage 5: Wie escapet EL HTML? Ist es wirklich XSS-sicher?
Ja, EL escapet automatisch HTML-Sonderzeichen!
Beispiel:
// User gibt ein:
String malicious = "<script>alert('XSS')</script>";
request.setAttribute("userInput", malicious);
Mit Scriptlet (GEFÄHRLICH!):
<%= request.getAttribute("userInput") %>
Ergebnis im HTML:
<script>alert('XSS')</script>
→ Script wird ausgeführt! XSS-Angriff!
Mit EL (SICHER!):
${userInput}
Ergebnis im HTML:
<script>alert('XSS')</script>
→ Script wird als Text angezeigt! Sicher!
Wichtig: Das gilt für normale EL. Wenn du JSTL <c:out> verwendest mit escapeXml="false", wird NICHT escaped – also Vorsicht!
Frage 6: Was passiert, wenn ich in EL durch Null teile?
EL wirft keine ArithmeticException!
${10 / 0} <!-- Ergebnis: Infinity -->
${0 / 0} <!-- Ergebnis: NaN (Not a Number) -->
${10 % 0} <!-- Ergebnis: NaN -->
Das ist anders als in Java:
int result = 10 / 0; // ArithmeticException!
EL ist fehlertoleranter! Ob das gut oder schlecht ist… kommt drauf an. 😄
Frage 7: Bernd meinte mal, „JSPs sind legacy, echte Devs nutzen Thymeleaf oder React“. Hat er recht?
Lowkey… sowohl ja als auch nein! Real talk:
Bernd hat teilweise recht:
- Moderne Frameworks wie React, Vue, Angular sind standard für neue Web-Apps
- Thymeleaf ist die modernere Template-Engine für Java (auch in Spring Boot Standard)
- JSPs werden in NEW Projects seltener eingesetzt
ABER:
- Millionen von Enterprise-Anwendungen laufen auf JSPs
- Banks, Versicherungen, große Konzerne – alle haben JSP-Code
- Du WIRST in deiner Karriere JSPs maintainen müssen
- JSPs sind nicht „tot“, nur nicht mehr cutting-edge
Meine Take:
- Für NEW Projects: Ja, moderne Alternativen sind besser (React + REST API, oder Thymeleaf)
- Für BESTEHENDE Projekte: JSPs funktionieren gut und sind battle-tested
- Für dein LERNEN: JSPs verstehen = Fundament für alle Template-Engines
Bernd arbeitet mit modernem Stack (Spring Boot + React). Aber selbst er musste mal Legacy-JSP-Code anfassen – und war froh, dass er’s gelernt hatte!
Bottom line: Lerne JSPs. Nutze moderne Tools für neue Projekte. Aber sei bereit, beide Welten zu beherrschen! 💪
📚 Quiz-Lösungen
Hier sind die Antworten zum Quiz von oben:
Frage 1: Erkläre den JSP-Lifecycle. Was sind die drei Schritte?
Antwort:
Der JSP-Lifecycle beschreibt, wie eine JSP-Datei zu einer lauffähigen Servlet-Klasse wird:
1. Translation (JSP → Java):
- Die JSP-Datei (
hello.jsp) wird in eine Java-Servlet-Klasse übersetzt (hello_jsp.java) - HTML-Code wird zu
out.write()Statements - EL (
${...}) wird zu Java-Code übersetzt - Direktiven, Scriptlets, etc. werden in Java-Code konvertiert
Wo liegt die generierte Datei?
- Payara:
C:\payara6\glassfish\domains\domain1\generated\jsp\
2. Compilation (Java → Bytecode):
- Die generierte Java-Datei wird zu Bytecode kompiliert
- Ergebnis:
hello_jsp.class - Diese Klasse erweitert
HttpServletund implementiert_jspService()Methode
3. Execution (Servlet läuft):
- Wenn ein Request kommt, lädt der Container die kompilierte Klasse
- Ruft
_jspService()Methode auf (wiedoGet()/doPost()bei normalen Servlets) - Generiert HTML-Response und schickt sie zurück
Wichtig zu verstehen:
- JSPs SIND Servlets! Nur mit angenehmerer Syntax
- Die drei Schritte passieren nur einmal (beim ersten Zugriff oder beim Deployment)
- Danach läuft die kompilierte Version (schnell!)
Frage 2: Was ist der Unterschied zwischen On-Demand-Compilation und Pre-Compilation?
Antwort:
On-Demand-Compilation (Standard in DEV):
Wie es funktioniert:
- JSP wird beim ersten Zugriff übersetzt und kompiliert
- Container merkt Änderungen an JSP-Dateien automatisch
- Recompiled JSP wenn Datei geändert wurde
Vorteile:
- ✅ Entwickler-freundlich: Änderung → Reload → Sofort sichtbar
- ✅ Kein Deployment nötig bei JSP-Änderungen
- ✅ Schneller Entwicklungs-Zyklus
Nachteile:
- ❌ Erster Request ist langsam (Translation + Compilation)
- ❌ User könnte Verzögerung bemerken
- ❌ Compilation-Fehler erst zur Laufzeit sichtbar
Pre-Compilation (Standard in PROD):
Wie es funktioniert:
- JSPs werden beim Deployment/Build übersetzt und kompiliert
- Fertig kompilierte
.classDateien werden ausgeliefert - Keine Recompilation zur Laufzeit
Vorteile:
- ✅ Schnell: Keine Verzögerung beim ersten Request
- ✅ Compilation-Fehler werden beim Build erkannt (nicht erst in Production!)
- ✅ Sicherer: Kein Source-Code (JSPs) auf Production-Server nötig
Nachteile:
- ❌ JSP-Änderungen = Neues Deployment nötig
- ❌ Langsamer Entwicklungs-Zyklus
Wann was verwenden?
| Umgebung | Modus | Grund |
|---|---|---|
| DEV/TEST | On-Demand | Schnelle Iteration, sofortiges Feedback |
| STAGING | Pre-Compile | Production-ähnlich testen |
| PRODUCTION | Pre-Compile | Performance, Fehler früh erkennen |
Wie aktiviert man Pre-Compilation?
Maven Plugin:
<plugin>
<groupId>org.apache.sling</groupId>
<artifactId>jspc-maven-plugin</artifactId>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>jspc</goal>
</goals>
</execution>
</executions>
</plugin>
Frage 3: Nenne 5 implizite Objekte in JSPs und erkläre, wofür sie verwendet werden.
Antwort:
Die 9 impliziten JSP-Objekte:
1. request (HttpServletRequest):
- Typ:
jakarta.servlet.http.HttpServletRequest - Verwendung: Zugriff auf Request-Parameter, Headers, Attribute
- Beispiel:
<% String name = request.getParameter("name"); String userAgent = request.getHeader("User-Agent"); %> - In EL:
${param.name},${header['User-Agent']}
2. response (HttpServletResponse):
- Typ:
jakarta.servlet.http.HttpServletResponse - Verwendung: Response-Header setzen, Redirects, Cookies
- Beispiel:
<% response.setHeader("Cache-Control", "no-cache"); response.addCookie(new Cookie("theme", "dark")); %> - Achtung: In modernen JSPs selten direkt verwendet (Controller macht das!)
3. session (HttpSession):
- Typ:
jakarta.servlet.http.HttpSession - Verwendung: Session-Daten speichern/lesen (Login-Status, Warenkorb, etc.)
- Beispiel:
<% User user = (User) session.getAttribute("currentUser"); session.setAttribute("lastPage", request.getRequestURI()); %> - In EL:
${sessionScope.currentUser}
4. application (ServletContext):
- Typ:
jakarta.servlet.ServletContext - Verwendung: Application-weite Daten, Context-Parameter
- Beispiel:
<% String dbUrl = application.getInitParameter("database.url"); int visitCount = (Integer) application.getAttribute("visitCount"); %> - In EL:
${initParam['database.url']},${applicationScope.visitCount}
5. out (JspWriter):
- Typ:
jakarta.servlet.jsp.JspWriter - Verwendung: Direkt HTML ausgeben (wie PrintWriter, aber gepuffert)
- Beispiel:
<% out.println("<p>Hello, World!</p>"); out.print("Current time: " + new Date()); %> - Achtung: Mit EL brauchst du
outfast nie!
Weitere implizite Objekte:
6. pageContext (PageContext):
- Zugriff auf alle Scopes, Request, Response, Session, etc.
- Beispiel:
${pageContext.request.contextPath}
7. page (Object):
- Die JSP selbst (this)
- Selten verwendet
8. config (ServletConfig):
- Servlet-Konfiguration
- Beispiel:
config.getServletName()
9. exception (Throwable):
- Nur verfügbar in Error-Pages (
isErrorPage="true") - Beispiel:
${exception.message}
Frage 4: Was ist der Unterschied zwischen <%@ include %> und <jsp:include>?
Antwort:
Zwei Arten, andere Dateien einzubinden:
<%@ include file="..." %> (Include-Direktive):
Wann passiert es?
- Compile-Time (Translation-Phase)
- Vor der Kompilierung
Wie funktioniert es?
- Dateiinhalt wird direkt eingefügt (wie Copy/Paste)
- Beide Dateien werden zu EINER Servlet-Klasse kompiliert
Beispiel:
main.jsp:
<%@ include file="header.jsp" %> <h1>Main Content</h1> <%@ include file="footer.jsp" %>
Wird kompiliert als wäre es:
<!-- header.jsp Inhalt --> <h1>Main Content</h1> <!-- footer.jsp Inhalt -->
Vorteile:
- ✅ Schneller (nur eine Servlet-Klasse)
- ✅ Gut für statische Inhalte (Header, Footer)
Nachteile:
- ❌ Änderungen an
header.jsp→ main.jsp muss recompiled werden! - ❌ Nicht dynamisch (kann keine Parameter übergeben)
<jsp:include page="..." /> (Include-Action):
Wann passiert es?
- Runtime (Request-Zeit)
- Während der Ausführung
Wie funktioniert es?
- Separate Servlet-Klasse wird aufgerufen
- Output wird eingefügt
Beispiel:
<jsp:include page="header.jsp" /> <h1>Main Content</h1> <jsp:include page="footer.jsp" />
Mit Parametern:
<jsp:include page="userInfo.jsp">
<jsp:param name="userId" value="${user.id}" />
</jsp:include>
Vorteile:
- ✅ Dynamisch (kann zur Laufzeit entscheiden, was included wird)
- ✅ Kann Parameter übergeben
- ✅ Änderungen sofort sichtbar (keine Recompilation nötig)
Nachteile:
- ❌ Etwas langsamer (separater Request)
Vergleich:
| Feature | <%@ include %> | <jsp:include> |
|---|---|---|
| Zeitpunkt | Compile-Time | Runtime |
| Geschwindigkeit | Schneller | Etwas langsamer |
| Flexibilität | Statisch | Dynamisch |
| Parameter | Nein | Ja |
| Recompilation | Ja, bei Änderung | Nein |
| Use-Case | Header/Footer (statisch) | Dynamische Komponenten |
Wann was verwenden?
<%@ include %>:
- Statische Header/Footer
- CSS/JS die sich nie ändern
- Wiederverwendbare Code-Snippets
<jsp:include>:
- Dynamische Komponenten (User-Info, Widgets)
- Wenn Parameter übergeben werden müssen
- Wenn Inhalt zur Laufzeit ausgewählt wird
Best Practice: In modernen Apps meist <jsp:include> verwenden (flexibler!)
Frage 5: Warum solltest du Scriptlets vermeiden? Nenne mindestens 3 Gründe.
Antwort:
Scriptlets = <% ... %> und <%= ... %>
Warum sind sie problematisch?
1. Spaghetti-Code (Unlesbar):
Java-Code und HTML gemischt:
<!-- SCHLECHT: -->
<html>
<body>
<%
List<Product> products = (List<Product>) request.getAttribute("products");
if (products != null) {
for (Product p : products) {
if (p.getStock() > 0) {
%>
<div class="product">
<h2><%= p.getName() %></h2>
<p>$<%= p.getPrice() %></p>
</div>
<%
}
}
}
%>
</body>
</html>
Probleme:
- Java und HTML vermischt → schwer zu lesen
- Designer können nicht damit arbeiten
- Syntax-Highlighting funktioniert nicht richtig
2. Keine Trennung von Concerns (Model 1 Problem):
Business-Logik in View:
<!-- SCHLECHT: Business-Logik in JSP! -->
<%
ProductDAO dao = new ProductDAO();
List<Product> products = dao.getAll();
// Discount berechnen (Business-Logik!)
for (Product p : products) {
if (p.getPrice() > 100) {
double discount = p.getPrice() * 0.1;
p.setPrice(p.getPrice() - discount);
}
}
%>
Warum schlecht?
- View sollte NUR zeigen, nicht berechnen!
- Business-Logik gehört in Service/Controller
- Nicht testbar (wie testet man JSPs?)
- Nicht wiederverwendbar
3. Sicherheitsprobleme (XSS):
Kein automatisches Escaping:
<!-- GEFÄHRLICH: -->
<p>Welcome, <%= request.getParameter("name") %></p>
Wenn name = <script>alert('XSS')</script> ist → Script wird ausgeführt!
Mit EL (sicher):
<p>Welcome, ${param.name}</p> <!-- Automatisch escaped! -->
4. NullPointerException-Gefahr:
<!-- CRASHT wenn user null ist: --> <p>Welcome, <%= user.getName() %></p> <!-- NullPointerException! -->
Mit EL (null-safe):
<p>Welcome, ${user.name}</p> <!-- Zeigt nichts wenn null -->
5. Schwer zu warten:
Nach 3 Monaten zurückkommen:
<%
// Was macht dieser Code??
if (request.getAttribute("x") != null) {
Object o = request.getAttribute("x");
if (o instanceof List) {
List l = (List) o;
if (!l.isEmpty()) {
// ... 50 Zeilen später ...
}
}
}
%>
Viel Glück beim Verstehen! 😰
6. Kein Syntax-Checking vor Runtime:
<%
// Tippfehler - wird erst zur Laufzeit entdeckt!
String name = request.getParamter("name"); // ParamTer statt ParameTer
%>
Compiler sieht das nicht → erst beim Request crasht es!
7. Performance (minimal, aber vorhanden):
Scriptlets müssen bei jedem Request ausgeführt werden. EL-Expressions werden zur Compile-Time optimiert.
Die bessere Alternative: Expression Language (EL)
<!-- BESSER: -->
<html>
<body>
<c:forEach var="product" items="${products}">
<c:if test="${product.stock > 0}">
<div class="product">
<h2>${product.name}</h2>
<p>$${product.price}</p>
</div>
</c:if>
</c:forEach>
</body>
</html>
Vorteile:
- ✅ Lesbar (sieht aus wie HTML mit extra Tags)
- ✅ Null-safe
- ✅ Automatisches HTML-Escaping
- ✅ Designer-freundlich
- ✅ Testbar (Business-Logik im Controller!)
- ✅ Keine NullPointerExceptions
- ✅ Separation of Concerns
Fazit: Scriptlets = Legacy! Verwende EL + JSTL für moderne, saubere JSPs!
Frage 6: Was macht Expression Language (EL) null-safe? Warum ist das wichtig?
Antwort:
EL-Null-Safety bedeutet:
Wenn ein Ausdruck in EL null ergibt, crasht die Seite NICHT – stattdessen wird ein leerer String zurückgegeben!
Beispiel:
// Im Servlet: user ist null!
request.setAttribute("user", null);
Mit Scriptlet (CRASHT!):
<%= user.getName() %> <!-- NullPointerException! → 500 Error → Seite kaputt! -->
Mit EL (SICHER!):
${user.name}
<!-- Ergebnis: "" (leerer String) → Seite funktioniert! -->
Wie funktioniert EL Null-Safety?
Null-Checks auf JEDEM Level der Property-Chain:
${user.address.street.name}
EL macht intern:
Object user = pageContext.findAttribute("user");
if (user == null) return "";
Object address = getProperty(user, "address");
if (address == null) return "";
Object street = getProperty(address, "street");
if (street == null) return "";
Object name = getProperty(street, "name");
if (name == null) return "";
return name.toString();
Jeder Schritt wird geprüft!
Warum ist das wichtig?
1. Robuste Anwendungen:
Ohne Null-Safety:
<p>Welcome, <%= user.getName() %></p>
→ Wenn User nicht eingeloggt (user = null) → 500 Error → Ganze Seite kaputt!
Mit Null-Safety:
<p>Welcome, ${user.name}</p>
→ Wenn User nicht eingeloggt → Zeigt „Welcome, “ → Seite funktioniert weiter!
2. Weniger defensive Programmierung nötig:
Ohne EL:
<%
User user = (User) request.getAttribute("user");
String name = "Guest";
if (user != null && user.getName() != null) {
name = user.getName();
}
%>
<p>Welcome, <%= name %></p>
Mit EL:
<p>Welcome, ${not empty user.name ? user.name : 'Guest'}</p>
Viel kürzer und lesbarer!
3. Partielle Daten okay:
Manchmal hast du optionale Felder:
<!-- Product mit optionalem Bild -->
<div class="product">
<h2>${product.name}</h2>
<p>${product.description}</p>
<!-- Wenn kein Bild → zeigt nichts (kein Crash!) -->
<img src="${product.imageUrl}" alt="${product.name}">
</div>
Ohne Null-Safety müsstest du für JEDES optionale Feld einen Check machen!
4. Fehlertoleranz während Entwicklung:
Beim Entwickeln vergisst man manchmal, ein Attribute zu setzen:
// Servlet: Vergessen, product zu setzen!
// request.setAttribute("product", product); // Oops!
request.getRequestDispatcher("/product.jsp").forward(request, response);
Ohne Null-Safety: 500 Error, Seite kaputt, Debugging-Horror!
Mit Null-Safety: Seite zeigt leere Werte, aber crasht nicht → leichter zu debuggen!
Wichtige Ausnahme:
EL-Null-Safety gilt nur für Property-Access (${user.name}), NICHT für:
Arithmetische Operationen:
${null + 5} <!-- Ergebnis: 5 (null wird zu 0) -->
${null * 5} <!-- Ergebnis: 0 -->
${null / 5} <!-- Ergebnis: 0 -->
Vergleiche:
${null == null} <!-- Ergebnis: true -->
${null == 5} <!-- Ergebnis: false -->
Best Practice mit EL Null-Safety:
✅ Vertraue auf Null-Safety für Property-Access:
${user.name}
${product.description}
${order.customer.address.street}
✅ Nutze empty für explizite Null-Checks:
<c:if test="${not empty user}">
<p>Welcome, ${user.name}!</p>
</c:if>
<c:if test="${empty cart.items}">
<p>Your cart is empty.</p>
</c:if>
✅ Nutze Conditional Operator für Fallbacks:
${not empty user.name ? user.name : 'Guest'}
${not empty product.imageUrl ? product.imageUrl : '/images/default.png'}
❌ Verlasse dich NICHT auf Null-Safety für Business-Logik:
<!-- SCHLECHT: -->
${user.address.street} <!-- Wenn null, zeigt nichts - User weiß nicht warum! -->
<!-- BESSER: -->
<c:choose>
<c:when test="${not empty user.address}">
${user.address.street}
</c:when>
<c:otherwise>
<p>No address on file. <a href="/profile">Add address</a></p>
</c:otherwise>
</c:choose>
Fazit: EL-Null-Safety macht JSPs robust und fehlertoleranter! Nutze es, aber sei explizit wenn es um wichtige Daten geht!
Frage 7: Erkläre den Unterschied zwischen ${param.name} und ${paramValues.hobby}.
Antwort:
Beide greifen auf Request-Parameter zu – aber anders!
${param.name} – Einzelner Wert:
Verwendung: Wenn Parameter NUR EINMAL in der URL/Form vorkommt
Beispiel:
URL:
http://localhost:8080/app?name=Anna&age=25
Formular:
<form>
<input type="text" name="name" value="Anna">
<input type="number" name="age" value="25">
</form>
In JSP:
<p>Name: ${param.name}</p> <!-- Anna -->
<p>Age: ${param.age}</p> <!-- 25 -->
Was macht param?
- Entspricht:
request.getParameter("name") - Gibt einen String zurück (oder leeren String wenn nicht vorhanden)
${paramValues.hobby} – Mehrere Werte (Array):
Verwendung: Wenn Parameter MEHRMALS vorkommt (z.B. Checkboxes, Multi-Select)
Beispiel:
URL:
http://localhost:8080/app?hobby=Reading&hobby=Gaming&hobby=Coding
Formular:
<form>
<input type="checkbox" name="hobby" value="Reading"> Reading
<input type="checkbox" name="hobby" value="Gaming"> Gaming
<input type="checkbox" name="hobby" value="Coding"> Coding
</form>
In JSP:
<!-- FALSCH - zeigt nur ersten Wert! -->
<p>Hobby: ${param.hobby}</p> <!-- Nur "Reading" -->
<!-- RICHTIG - zeigt alle Werte! -->
<c:forEach var="h" items="${paramValues.hobby}">
<p>Hobby: ${h}</p>
</c:forEach>
<!-- Ergebnis:
Hobby: Reading
Hobby: Gaming
Hobby: Coding
-->
<!-- Oder einzeln zugreifen: -->
<p>First hobby: ${paramValues.hobby[0]}</p> <!-- Reading -->
<p>Second hobby: ${paramValues.hobby[1]}</p> <!-- Gaming -->
<p>Third hobby: ${paramValues.hobby[2]}</p> <!-- Coding -->
Was macht paramValues?
- Entspricht:
request.getParameterValues("hobby") - Gibt ein String-Array zurück
Vergleich:
| Feature | ${param.name} | ${paramValues.hobby} |
|---|---|---|
| Rückgabe-Typ | String | String[] (Array) |
| Entspricht | request.getParameter() | request.getParameterValues() |
| Verwendung | Single-Value Parameter | Multi-Value Parameter |
| Beispiel | Input-Felder, Select (single) | Checkboxes, Multi-Select |
Wann welches verwenden?
param verwenden für:
- Text-Inputs (
<input type="text">) - Single-Select Dropdowns (
<select>) - Radio-Buttons (nur einer ausgewählt)
- Hidden-Fields
- Einzelne URL-Parameter
paramValues verwenden für:
- Checkboxes (mehrere ausgewählt)
- Multi-Select Dropdowns (
<select multiple>) - Mehrere URL-Parameter mit gleichem Namen
Häufiger Fehler:
<!-- USER HAT MEHRERE HOBBIES AUSGEWÄHLT: -->
<!-- FALSCH - zeigt nur ersten Wert! -->
<p>Your hobbies: ${param.hobby}</p> <!-- Nur "Reading" -->
<!-- RICHTIG - zeigt alle Werte! -->
<p>Your hobbies:
<c:forEach var="h" items="${paramValues.hobby}" varStatus="status">
${h}<c:if test="${!status.last}">, </c:if>
</c:forEach>
</p>
<!-- Ergebnis: "Reading, Gaming, Coding" -->
Beispiel: Vollständiges Formular
HTML-Form:
<form action="submit" method="post">
<!-- Single-Value: -->
<input type="text" name="username" placeholder="Username">
<!-- Single-Value: -->
<select name="country">
<option value="DE">Germany</option>
<option value="US">USA</option>
</select>
<!-- Multi-Value: -->
<p>Interests:</p>
<input type="checkbox" name="interest" value="Tech"> Tech
<input type="checkbox" name="interest" value="Sports"> Sports
<input type="checkbox" name="interest" value="Music"> Music
<button type="submit">Submit</button>
</form>
JSP (Anzeige):
<h2>Your Submission:</h2>
<!-- Single-Value Parameters: -->
<p>Username: ${param.username}</p>
<p>Country: ${param.country}</p>
<!-- Multi-Value Parameter: -->
<p>Interests:</p>
<ul>
<c:forEach var="interest" items="${paramValues.interest}">
<li>${interest}</li>
</c:forEach>
</ul>
Fazit:
- Ein Wert? →
${param.xxx} - Mehrere Werte? →
${paramValues.xxx}+ forEach
Frage 8: Was ist der empty Operator in EL und wann würdest du ihn verwenden?
Antwort:
empty Operator prüft, ob etwas „leer“ ist.
Syntax:
${empty variable} <!-- true wenn leer -->
${not empty variable} <!-- true wenn NICHT leer -->
Was bedeutet „leer“?
Der empty Operator gibt true zurück wenn:
- Variable ist
null - String ist
""(leerer String) - Collection/List ist leer (size = 0)
- Array ist leer (length = 0)
- Map ist leer (size = 0)
Beispiele für verschiedene Datentypen:
Strings:
// Servlet:
request.setAttribute("name", "Anna"); // NICHT leer
request.setAttribute("emptyString", ""); // Leer
request.setAttribute("nullString", null); // Leer
<!-- JSP: -->
${empty name} <!-- false -->
${empty emptyString} <!-- true -->
${empty nullString} <!-- true -->
${not empty name} <!-- true -->
${not empty emptyString}<!-- false -->
Collections (Listen):
// Servlet:
List<Product> products = Arrays.asList(
new Product("Laptop"),
new Product("Mouse")
);
List<Product> emptyList = new ArrayList<>();
List<Product> nullList = null;
request.setAttribute("products", products);
request.setAttribute("emptyList", emptyList);
request.setAttribute("nullList", nullList);
<!-- JSP: -->
${empty products} <!-- false (2 items) -->
${empty emptyList} <!-- true (0 items) -->
${empty nullList} <!-- true (null) -->
${not empty products} <!-- true -->
Arrays:
// Servlet:
String[] colors = {"Red", "Green", "Blue"};
String[] emptyArray = {};
String[] nullArray = null;
request.setAttribute("colors", colors);
request.setAttribute("emptyArray", emptyArray);
request.setAttribute("nullArray", nullArray);
<!-- JSP: -->
${empty colors} <!-- false -->
${empty emptyArray} <!-- true -->
${empty nullArray} <!-- true -->
Maps:
// Servlet:
Map<String, String> config = new HashMap<>();
config.put("theme", "dark");
Map<String, String> emptyMap = new HashMap<>();
Map<String, String> nullMap = null;
request.setAttribute("config", config);
request.setAttribute("emptyMap", emptyMap);
request.setAttribute("nullMap", nullMap);
<!-- JSP: -->
${empty config} <!-- false (1 entry) -->
${empty emptyMap} <!-- true (0 entries) -->
${empty nullMap} <!-- true (null) -->
Wann empty verwenden?
Use-Case 1: Optionale Form-Felder anzeigen
<c:if test="${not empty param.name}">
<p>Hello, ${param.name}!</p>
</c:if>
<c:if test="${empty param.name}">
<p>Please enter your name.</p>
</c:if>
Use-Case 2: Listen auf Inhalt prüfen
<c:choose>
<c:when test="${not empty products}">
<ul>
<c:forEach var="product" items="${products}">
<li>${product.name}</li>
</c:forEach>
</ul>
</c:when>
<c:otherwise>
<p>No products found.</p>
</c:otherwise>
</c:choose>
Use-Case 3: Optionale Daten anzeigen
<!-- Nur anzeigen wenn User ein Profilbild hat -->
<c:if test="${not empty user.profileImageUrl}">
<img src="${user.profileImageUrl}" alt="Profile">
</c:if>
<!-- Nur anzeigen wenn User einen Bio-Text hat -->
<c:if test="${not empty user.bio}">
<p>${user.bio}</p>
</c:if>
Use-Case 4: Fallback-Werte
<!-- Conditional Operator mit empty -->
<p>Theme: ${not empty user.theme ? user.theme : 'default'}</p>
<p>Language: ${not empty user.language ? user.language : 'en'}</p>
Use-Case 5: Validierungs-Feedback
<form>
<input type="text" name="email" value="${param.email}">
<c:if test="${empty param.email && not empty param.submit}">
<p class="error">Email is required!</p>
</c:if>
</form>
Use-Case 6: Warenkorb-Check
<c:choose>
<c:when test="${not empty cart.items}">
<h2>Your Cart (${cart.items.size()} items)</h2>
<c:forEach var="item" items="${cart.items}">
<div>${item.productName} - ${item.quantity}x</div>
</c:forEach>
<button>Checkout</button>
</c:when>
<c:otherwise>
<p>Your cart is empty. <a href="/products">Start shopping</a></p>
</c:otherwise>
</c:choose>
Vergleich: empty vs. == null
empty ist besser als == null:
<!-- UMSTÄNDLICH: -->
<c:if test="${param.name != null && param.name != ''}">
<p>${param.name}</p>
</c:if>
<!-- BESSER: -->
<c:if test="${not empty param.name}">
<p>${param.name}</p>
</c:if>
Häufige Patterns mit empty:
1. „Wenn vorhanden, dann zeige“:
<c:if test="${not empty user}">
<p>Welcome, ${user.name}!</p>
</c:if>
2. „Wenn leer, dann Fallback“:
<p>${not empty products ? 'Products available' : 'No products yet'}</p>
3. „Zeige Liste ODER Nachricht“:
<c:choose>
<c:when test="${not empty items}">
<c:forEach var="item" items="${items}">
<!-- Zeige Items -->
</c:forEach>
</c:when>
<c:otherwise>
<p>Nothing to display.</p>
</c:otherwise>
</c:choose>
Fazit: empty ist DER Operator für „existiert/hat Inhalt?“-Checks in EL! Nutze ihn statt umständlichen != null && != "" Checks!
🎉 Tag 5 geschafft!
Du rockst! Main Character Energy! ✨
Real talk: JSPs und Expression Language sind ein riesiges Thema. Wenn dir der Kopf schwirrt – das ist völlig okay!
Das hast du heute gelernt:
- ✅ Was JSPs sind und wie sie zu Servlets kompiliert werden
- ✅ Der komplette JSP-Lifecycle (Translation → Compilation → Execution)
- ✅ Implizite Objekte in JSPs
- ✅ JSP-Direktiven (
<%@ page %>,<%@ include %>,<%@ taglib %>) - ✅ Warum Scriptlets schlecht sind und du sie vermeiden solltest
- ✅ Expression Language (EL) – die moderne Art
- ✅ EL-Syntax und alle impliziten EL-Objekte
- ✅ EL-Operatoren (arithmetisch, logisch, conditional, empty)
- ✅ Null-Safety in EL
- ✅ Best Practices für JSPs
Du kannst jetzt:
- Saubere JSPs ohne Java-Code schreiben
- Expression Language sicher verwenden
- Daten vom Controller in der View anzeigen
- Implizite Objekte nutzen
- MVC korrekt umsetzen (JSP = View!)
Honestly? Das ist HUGE! JSPs sind das Herzstück vieler Enterprise-Anwendungen. Du verstehst jetzt, wie die „View“ in MVC funktioniert.
Und das Beste: Du weißt, warum Scriptlets böse sind – und wie man es besser macht! 💪
🔮 Wie geht’s weiter?
Morgen (Tag 6): Java Beans, Actions, Scopes & Direktiven
Was dich erwartet:
- Was JavaBeans sind
- JSP-Actions (
<jsp:useBean>,<jsp:setProperty>,<jsp:getProperty>) - Forward vs. Include Actions
- Scopes im Detail (Page, Request, Session, Application)
- Wann du welchen Scope verwendest
- Scope-Management wird dein Game-Changer für State-Handling! 🔥
Brauchst du eine Pause?
Mach sie! EL-Syntax braucht Zeit zum Verinnerlichen.
Tipp für heute Abend:
Konvertiere ein altes Scriptlet-JSP zu EL. Das ist die beste Übung!
Learning by doing! 🎨
🔧 Troubleshooting
Problem: EL wird nicht ausgewertet – ${param.name} steht wörtlich im HTML
Lösung:
<!-- Prüfe web.xml Version --> <web-app version="6.0" ...> <!-- Muss >= 2.4 sein --> <!-- Oder explizit aktivieren: --> <%@ page isELIgnored="false" %>
Problem: NullPointerException in JSP
Lösung:
<!-- FALSCH - kann crashen: -->
<%= request.getAttribute("user").getName() %>
<!-- RICHTIG - null-safe: -->
${user.name}
<!-- Oder mit null-check: -->
${not empty user ? user.name : 'Guest'}
Problem: JSP findet Attribute nicht
Lösung:
// Servlet - prüfe Scope:
request.setAttribute("product", product); // Request-Scope
// Forward (NICHT Redirect!)
request.getRequestDispatcher("/WEB-INF/views/product.jsp")
.forward(request, response);
Problem: Änderungen an JSP werden nicht angezeigt
Lösung:
- Hard-Refresh im Browser (Ctrl+F5)
- Prüfe ob Pre-Compilation aktiv ist
- Lösche generated-Ordner manuell:
C:\payara6\glassfish\domains\domain1\generated\jsp\ - Server neu starten
Problem: JSTL-Tags werden nicht erkannt
Lösung:
<!-- Prüfe Taglib-Import: --> <%@ taglib prefix="c" uri="jakarta.tags.core" %> <!-- Jakarta EE 10+: jakarta.tags.* --> <!-- Alte Versionen: http://java.sun.com/jsp/jstl/core -->
Prüfe auch, ob JSTL-JAR im Classpath ist!
📚 Resources & Links
JSP & EL:
- Jakarta EE Tutorial: https://jakarta.ee/learn/
- JSP Specification: https://jakarta.ee/specifications/pages/
- EL Specification: https://jakarta.ee/specifications/expression-language/
Best Practices:
- JSP Best Practices: https://www.baeldung.com/jsp-best-practices
- Avoiding Scriptlets: https://stackoverflow.com/questions/3177733/how-to-avoid-java-code-in-jsp-files
JSTL (kommt Tag 8):
💬 Feedback?
War Tag 5 zu viel Syntax? Zu wenig Beispiele? Mehr Übungen gewünscht?
Schreib uns: feedback@java-developer.online
Wir wollen, dass du erfolgreich lernst!
Bis morgen! 👋
Elyndra
elyndra@java-developer.online
Senior Developer bei Java Fleet Systems Consulting
Java Web Basic – Tag 5 von 10
Teil der Java Fleet Learning-Serie
© 2025 Java Fleet Systems Consulting

