Java Web Basic – Tag 6 von 10
Von Elyndra Valen, Senior Developer bei Java Fleet Systems Consulting


JavaBeans

🗺️ Deine Position im Kurs

TagThemaStatus
1Java EE Überblick & HTTP ✅ Abgeschlossen
2HTTP-Protokoll Vertiefung & Zustandslosigkeit✅ Abgeschlossen
3Servlets & Servlet API✅ Abgeschlossen
4Deployment Descriptor & MVC vs Model 2✅ Abgeschlossen
5JSP & Expression Languages ✅ Abgeschlossen
→ 6Java Beans, Actions, Scopes & Direktiven👉 DU BIST HIER!
7Include-Action vs Include-Direktive📜 Kommt als nächstes
8JSTL – Java Standard Tag Libraries🔒 Noch nicht freigeschaltet
9Java Web und Datenbanken – Datasource🔒 Noch nicht freigeschaltet
10Connection Pools & JDBC in Web-Umgebungen🔒 Noch nicht freigeschaltet

Modul: Java Web Basic
Gesamt-Dauer: 10 Arbeitstage (je 8 Stunden)
Dein Ziel: JavaBeans verstehen, Actions nutzen und Scopes meistern


📋 Voraussetzungen für diesen Tag

Du brauchst:

  • ✅ JDK 21 LTS installiert
  • ✅ Apache NetBeans 22 (oder neuer)
  • ✅ Payara Server 6.x konfiguriert
  • ✅ Tag 1-5 abgeschlossen
  • ✅ JSP & Expression Language verstanden
  • ✅ MVC/Model 2 Architektur verstanden
  • ✅ Implizite Objekte kennen (${param}, ${requestScope}, etc.)

Tag verpasst?
Spring zurück zu Tag 5, um JSP und Expression Language zu lernen. Ohne EL wird heute schwer!

Setup-Probleme?
Schreib uns: elyndra.valen@java-developer.online


⚡ Das Wichtigste in 30 Sekunden

Heute lernst du:

  • ✅ Was JavaBeans sind und warum sie wichtig sind
  • ✅ JavaBeans Konventionen (getter/setter, no-arg constructor)
  • ✅ JSP Standard Actions (<jsp:useBean>, <jsp:setProperty>, <jsp:getProperty>)
  • ✅ Die 4 Scopes verstehen (page, request, session, application)
  • ✅ JSP-Direktiven im Detail (<%@ page %>, <%@ include %>, <%@ taglib %>)
  • ✅ Best Practices für saubere Views

Am Ende des Tages kannst du:

  • JavaBeans in JSPs verwenden
  • Actions richtig einsetzen
  • Scopes gezielt nutzen für Daten-Management
  • Direktiven konfigurieren
  • Saubere, wartbare JSPs schreiben

Zeit-Investment: ~8 Stunden
Schwierigkeitsgrad: Mittel (Konzepte + Praxis!)


👋 Willkommen zu Tag 6!

Hi! 👋

Elyndra hier. Heute wird’s richtig praktisch!

Kurzwiederholung: Challenge von Tag 5

Gestern solltest du eine JSP erstellen, die Produktdaten anzeigt und Expression Language verwendet. Falls du es noch nicht gemacht hast, hier die Musterlösung:

Controller: ProductServlet.java

package com.example.controller;

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.*;
import java.io.IOException;
import java.util.*;

@WebServlet("/products")
public class ProductServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest request, 
                        HttpServletResponse response) 
                        throws ServletException, IOException {
        
        // Simuliere Produktdaten
        List<Map<String, Object>> products = new ArrayList<>();
        
        Map<String, Object> product1 = new HashMap<>();
        product1.put("id", 1);
        product1.put("name", "Laptop");
        product1.put("price", 899.99);
        products.add(product1);
        
        Map<String, Object> product2 = new HashMap<>();
        product2.put("id", 2);
        product2.put("name", "Smartphone");
        product2.put("price", 599.99);
        products.add(product2);
        
        // Setze im Request-Scope
        request.setAttribute("products", products);
        
        // Forward zu JSP
        request.getRequestDispatcher("/WEB-INF/views/products.jsp")
               .forward(request, response);
    }
}

View: /WEB-INF/views/products.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
    <title>Produktliste</title>
</head>
<body>
    <h1>Unsere Produkte</h1>
    
    <table border="1">
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Preis</th>
        </tr>
        <c:forEach var="product" items="${products}">
            <tr>
                <td>${product.id}</td>
                <td>${product.name}</td>
                <td>${product.price} €</td>
            </tr>
        </c:forEach>
    </table>
</body>
</html>

Hast du’s selbst geschafft? Großartig! 🎉
Hattest du Schwierigkeiten? Kein Problem – vergleiche deinen Code mit der Lösung.


Und jetzt geht’s weiter mit Tag 6! 🚀

Warum JavaBeans wichtig sind:

Gestern hast du gelernt, wie man Daten mit Map<String, Object> in JSPs übergibt. Das funktioniert, aber es gibt ein Problem: Maps sind nicht typsicher, unübersichtlich bei vielen Eigenschaften und nicht wiederverwendbar.

JavaBeans lösen dieses Problem elegant. Sie sind wiederverwendbare, typsichere Datencontainer – perfekt für das Model in MVC.

Was dich heute erwartet:

Wir schauen uns an, wie du JavaBeans definierst und in JSPs verwendest. Du lernst die JSP Standard Actions kennen – spezielle Tags, die mit Beans arbeiten. Dann vertiefen wir die verschiedenen Scopes (page, request, session, application) und verstehen, wann du welchen Scope brauchst. Zum Schluss schauen wir uns JSP-Direktiven im Detail an.

Keine Sorge:

JavaBeans klingen kompliziert, sind aber eigentlich nur POJOs (Plain Old Java Objects) mit ein paar Konventionen. Und die Actions? Die machen dein Leben einfacher, nicht schwerer!

Los geht’s! 🔧


🟢 GRUNDLAGEN: JavaBeans verstehen

Was sind JavaBeans?

Definition:

Ein JavaBean ist eine wiederverwendbare Java-Klasse, die bestimmten Konventionen folgt:

  1. Public class – Die Klasse muss öffentlich sein
  2. No-arg Constructor – Ein Konstruktor ohne Parameter
  3. Private properties – Felder sind privat
  4. Public getter/setter – Zugriff über getter/setter-Methoden
  5. Serializable – Optional, aber empfohlen

Analogie:

Stell dir vor, ein JavaBean ist wie ein Formular mit Feldern:

  • Die Felder sind privat (niemand kann direkt reinschreiben)
  • Es gibt Getter (zum Lesen der Felder)
  • Es gibt Setter (zum Beschreiben der Felder)
  • Das Formular hat eine Standard-Version (no-arg Constructor)

Beispiel: Ein einfaches JavaBean

package com.example.model;

import java.io.Serializable;

public class Product implements Serializable {
    
    // 1. Private properties
    private int id;
    private String name;
    private double price;
    private String category;
    
    // 2. No-arg Constructor (PFLICHT!)
    public Product() {
        // Standard-Werte können hier gesetzt werden
    }
    
    // 3. Constructor mit Parametern (optional, aber praktisch)
    public Product(int id, String name, double price, String category) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.category = category;
    }
    
    // 4. Public Getter/Setter
    public int getId() {
        return id;
    }
    
    public void setId(int id) {
        this.id = id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public double getPrice() {
        return price;
    }
    
    public void setPrice(double price) {
        this.price = price;
    }
    
    public String getCategory() {
        return category;
    }
    
    public void setCategory(String category) {
        this.category = category;
    }
    
    // 5. toString() für Debugging (optional, aber hilfreich)
    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", category='" + category + '\'' +
                '}';
    }
}

Was passiert hier?

  1. private Felder: Niemand kann direkt auf product.price zugreifen
  2. No-arg Constructor: new Product() funktioniert
  3. Getter: product.getPrice() liest den Preis
  4. Setter: product.setPrice(99.99) setzt den Preis
  5. Serializable: Das Bean kann gespeichert/übertragen werden

Warum brauchst du JavaBeans?

Vorteile gegenüber Maps:

// ❌ SCHLECHT: Mit Map
Map<String, Object> product = new HashMap<>();
product.put("name", "Laptop");
product.put("price", 899.99);
// Typen nicht sicher!
// Tippfehler möglich: "priice" statt "price"
// IDE hat keine Auto-Completion

// ✅ GUT: Mit JavaBean
Product product = new Product();
product.setName("Laptop");
product.setPrice(899.99);
// Typsicher!
// IDE gibt Fehler bei Tippfehlern
// Auto-Completion funktioniert

In der Praxis bedeutet das:

  1. Typsicherheit: Compiler prüft deine Typen
  2. Lesbarkeit: product.getName() ist klarer als product.get("name")
  3. Refactoring: Umbenennen funktioniert in der ganzen Codebase
  4. Dokumentation: Klare Struktur der Daten
  5. Wiederverwendbarkeit: Beans können überall genutzt werden

Dein erstes JavaBean in einer JSP

Schritt 1: Bean erstellen

Erstelle die Datei src/main/java/com/example/model/User.java:

package com.example.model;

import java.io.Serializable;

public class User implements Serializable {
    
    private String username;
    private String email;
    private String role;
    
    // No-arg Constructor
    public User() {}
    
    // Constructor mit Parametern
    public User(String username, String email, String role) {
        this.username = username;
        this.email = email;
        this.role = role;
    }
    
    // Getter/Setter
    public String getUsername() {
        return username;
    }
    
    public void setUsername(String username) {
        this.username = username;
    }
    
    public String getEmail() {
        return email;
    }
    
    public void setEmail(String email) {
        this.email = email;
    }
    
    public String getRole() {
        return role;
    }
    
    public void setRole(String role) {
        this.role = role;
    }
}

Schritt 2: Servlet erstellt User-Bean

Erstelle src/main/java/com/example/controller/UserServlet.java:

package com.example.controller;

import com.example.model.User;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.*;
import java.io.IOException;

@WebServlet("/user")
public class UserServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest request, 
                        HttpServletResponse response) 
                        throws ServletException, IOException {
        
        // User-Bean erstellen
        User user = new User("nova", "nova@java-developer.online", "Developer");
        
        // Im Request-Scope speichern
        request.setAttribute("currentUser", user);
        
        // Forward zu JSP
        request.getRequestDispatcher("/WEB-INF/views/user.jsp")
               .forward(request, response);
    }
}

Schritt 3: JSP nutzt Expression Language

Erstelle /WEB-INF/views/user.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
    <title>User Profile</title>
</head>
<body>
    <h1>User Profile</h1>
    
    <!-- Expression Language greift auf Getter zu! -->
    <p>Username: ${currentUser.username}</p>
    <p>Email: ${currentUser.email}</p>
    <p>Role: ${currentUser.role}</p>
    
    <!-- EL ruft automatisch getUsername(), getEmail(), getRole() auf! -->
</body>
</html>

Was passiert hier?

  1. Servlet: Erstellt User-Bean, speichert es im Request-Scope
  2. JSP: Nutzt Expression Language ${currentUser.username}
  3. EL-Magic: ${currentUser.username} ruft automatisch currentUser.getUsername() auf!
  4. Kein Java-Code in JSP: Die View bleibt sauber!

Teste es:

http://localhost:8080/YourApp/user

Du siehst:

User Profile
Username: nova
Email: nova@java-developer.online
Role: Developer

🟢 GRUNDLAGEN: JSP Standard Actions

Was sind JSP Standard Actions?

Definition:

JSP Standard Actions sind spezielle XML-Tags, die direkt in JSPs verwendet werden können. Sie beginnen mit <jsp:...> und bieten vordefinierte Funktionen für häufige Aufgaben.

Die wichtigsten Actions:

  1. <jsp:useBean> – Bean erstellen oder finden
  2. <jsp:setProperty> – Property setzen
  3. <jsp:getProperty> – Property auslesen
  4. <jsp:include> – Andere Ressource einbinden
  5. <jsp:forward> – Zu anderer Ressource weiterleiten

Heute fokussieren wir uns auf die ersten drei – die sogenannten Bean Actions.

<jsp:useBean> – Bean erstellen oder laden

Syntax:

<jsp:useBean id="beanName" 
             class="package.ClassName" 
             scope="page|request|session|application" />

Was macht es?

  1. Sucht im angegebenen Scope nach einem Bean mit dem Namen beanName
  2. Falls gefunden: Verwendet das existierende Bean
  3. Falls nicht gefunden: Erstellt neues Bean mit new ClassName()

Beispiel:

<!-- Erstellt oder findet User-Bean im Request-Scope -->
<jsp:useBean id="user" class="com.example.model.User" scope="request" />

<!-- Jetzt kannst du das Bean verwenden -->
<p>Username: ${user.username}</p>

Wichtig:

  • id: Name des Beans (entspricht request.getAttribute("user"))
  • class: Vollqualifizierter Klassenname
  • scope: Wo wird das Bean gespeichert? (default: page)

<jsp:setProperty> – Properties setzen

Syntax:

<!-- Einzelne Property setzen -->
<jsp:setProperty name="beanName" property="propertyName" value="value" />

<!-- Property aus Request-Parameter setzen -->
<jsp:setProperty name="beanName" property="propertyName" />

<!-- ALLE Properties aus Request-Parametern setzen -->
<jsp:setProperty name="beanName" property="*" />

Beispiel 1: Manuell setzen

<jsp:useBean id="user" class="com.example.model.User" scope="request" />

<jsp:setProperty name="user" property="username" value="nova" />
<jsp:setProperty name="user" property="email" value="nova@java-developer.online" />
<jsp:setProperty name="user" property="role" value="Developer" />

Beispiel 2: Aus Request-Parameter

<!-- URL: /form.jsp?username=nova&email=nova@java-developer.online -->

<jsp:useBean id="user" class="com.example.model.User" scope="request" />

<!-- Setzt alle Properties automatisch aus Request-Parametern -->
<jsp:setProperty name="user" property="*" />

<!-- Jetzt ist user.username = "nova", user.email = "nova@java-developer.online" -->

Was passiert bei property="*"?

JSP schaut sich ALLE Request-Parameter an und versucht, sie mit Bean-Properties zu matchen:

  • Request-Parameter usernamesetUsername() wird aufgerufen
  • Request-Parameter emailsetEmail() wird aufgerufen
  • Fehlende Parameter werden ignoriert

<jsp:getProperty> – Properties auslesen

Syntax:

<jsp:getProperty name="beanName" property="propertyName" />

Beispiel:

<jsp:useBean id="user" class="com.example.model.User" scope="request" />

<p>Username: <jsp:getProperty name="user" property="username" /></p>
<p>Email: <jsp:getProperty name="user" property="email" /></p>

Aber:

Expression Language ist meistens besser:

<!-- ✅ BESSER: Expression Language -->
<p>Username: ${user.username}</p>
<p>Email: ${user.email}</p>

<!-- ❌ Veraltet: getProperty -->
<p>Username: <jsp:getProperty name="user" property="username" /></p>

Wann <jsp:getProperty> verwenden?

Fast nie! Expression Language ist kürzer, lesbarer und moderner. <jsp:getProperty> ist Legacy-Code aus JSP 1.x-Zeiten.

Komplettes Beispiel: Formular mit Bean Actions

Formular: form.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
    <title>User Registration</title>
</head>
<body>
    <h1>Register</h1>
    
    <form action="register.jsp" method="post">
        <label>Username:</label>
        <input type="text" name="username" required /><br/>
        
        <label>Email:</label>
        <input type="email" name="email" required /><br/>
        
        <label>Role:</label>
        <select name="role">
            <option value="User">User</option>
            <option value="Developer">Developer</option>
            <option value="Admin">Admin</option>
        </select><br/>
        
        <button type="submit">Register</button>
    </form>
</body>
</html>

Verarbeitung: register.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
    <title>Registration Successful</title>
</head>
<body>
    <h1>Registration Successful!</h1>
    
    <!-- Bean erstellen/laden -->
    <jsp:useBean id="user" class="com.example.model.User" scope="session" />
    
    <!-- ALLE Properties aus Request-Parametern setzen -->
    <jsp:setProperty name="user" property="*" />
    
    <!-- Anzeigen mit Expression Language -->
    <p>Welcome, ${user.username}!</p>
    <p>Your email: ${user.email}</p>
    <p>Your role: ${user.role}</p>
    
    <p><a href="profile.jsp">View Profile</a></p>
</body>
</html>

Profil: profile.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
    <title>Your Profile</title>
</head>
<body>
    <h1>Your Profile</h1>
    
    <!-- Bean aus Session holen -->
    <jsp:useBean id="user" class="com.example.model.User" scope="session" />
    
    <!-- Bean existiert bereits in Session - wird wiederverwendet -->
    <p>Username: ${user.username}</p>
    <p>Email: ${user.email}</p>
    <p>Role: ${user.role}</p>
</body>
</html>

Was passiert hier?

  1. form.jsp: Zeigt Formular
  2. User gibt Daten ein: username=nova, email=nova@java-developer.online, role=Developer
  3. register.jsp:
    • <jsp:useBean> erstellt neues User-Bean im Session-Scope
    • <jsp:setProperty property="*"> setzt alle Properties aus Request-Parametern
    • Bean wird in Session gespeichert
  4. profile.jsp:
    • <jsp:useBean> findet existierendes Bean in Session
    • Kein neues Bean wird erstellt!
    • Daten sind noch da

🟢 GRUNDLAGEN: Die 4 Scopes verstehen

Was sind Scopes?

Definition:

Scopes definieren die Lebensdauer und Sichtbarkeit von Objekten in einer Webanwendung.

Die 4 Scopes:

ScopeLebensdauerSichtbarkeitUse-Case
pageNur diese JSPNur diese JSPTemporäre Daten für diese Seite
requestEin RequestAlle inkludierten/forwarded JSPsDaten für einen Request (MVC!)
sessionEine User-SessionAlle Requests des UsersUser-spezifische Daten (Login, Warenkorb)
applicationWebapp-LifetimeAlle UserGlobale Daten (Konfiguration, Zähler)

Page Scope

Lebensdauer: Nur diese JSP-Seite

Java-Code:

pageContext.setAttribute("message", "Hello");

JSP:

<jsp:useBean id="temp" class="com.example.model.User" scope="page" />

Expression Language:

${pageScope.message}
<!-- oder einfach -->
${message}

Wann verwenden?

  • Temporäre Variablen nur für diese JSP
  • Zwischenergebnisse von Berechnungen
  • Loop-Variablen

Beispiel:

<%
    // Nur für diese Seite
    pageContext.setAttribute("greeting", "Welcome");
%>

<h1>${greeting}</h1>

<!-- Wenn User Seite neu lädt: greeting ist weg -->

Request Scope

Lebensdauer: Ein HTTP-Request (inkl. Forward)

Java-Code:

request.setAttribute("product", product);
request.getRequestDispatcher("/view.jsp").forward(request, response);

JSP:

<jsp:useBean id="product" class="com.example.model.Product" scope="request" />

Expression Language:

${requestScope.product.name}
<!-- oder einfach -->
${product.name}

Wann verwenden?

  • MVC Pattern: Controller setzt Daten, View zeigt sie an
  • Daten, die nur für diesen Request relevant sind
  • Suchergebnisse, Formular-Validierung

Beispiel:

// Servlet
@WebServlet("/search")
public class SearchServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, 
                        HttpServletResponse response) 
                        throws ServletException, IOException {
        
        String query = request.getParameter("q");
        List<Product> results = searchProducts(query);
        
        // Request-Scope!
        request.setAttribute("results", results);
        request.setAttribute("query", query);
        
        request.getRequestDispatcher("/WEB-INF/views/results.jsp")
               .forward(request, response);
    }
}
<!-- results.jsp -->
<h1>Search Results for: ${query}</h1>

<c:forEach var="product" items="${results}">
    <p>${product.name} - ${product.price} €</p>
</c:forEach>

<!-- Bei neuem Request: results ist weg -->

Session Scope

Lebensdauer: Eine User-Session (typisch 30 Minuten Inaktivität)

Java-Code:

HttpSession session = request.getSession();
session.setAttribute("user", user);

JSP:

<jsp:useBean id="user" class="com.example.model.User" scope="session" />

Expression Language:

${sessionScope.user.username}
<!-- oder einfach -->
${user.username}

Wann verwenden?

  • Login-Daten: Eingeloggter User
  • Warenkorb: Shopping Cart
  • User-Einstellungen: Sprache, Theme
  • Wizard-Daten: Multi-Step-Formulare

Beispiel: Login

// LoginServlet
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, 
                         HttpServletResponse response) 
                         throws ServletException, IOException {
        
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        
        User user = authenticate(username, password);
        
        if (user != null) {
            // Session-Scope!
            HttpSession session = request.getSession();
            session.setAttribute("currentUser", user);
            
            response.sendRedirect("dashboard.jsp");
        } else {
            response.sendRedirect("login.jsp?error=1");
        }
    }
}
<!-- dashboard.jsp -->
<jsp:useBean id="currentUser" class="com.example.model.User" scope="session" />

<h1>Welcome, ${currentUser.username}!</h1>

<!-- User bleibt eingeloggt über mehrere Requests -->

Logout:

// LogoutServlet
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, 
                        HttpServletResponse response) 
                        throws ServletException, IOException {
        
        HttpSession session = request.getSession(false);
        if (session != null) {
            session.invalidate(); // Session löschen!
        }
        
        response.sendRedirect("login.jsp");
    }
}

Application Scope

Lebensdauer: Gesamte Webapp-Laufzeit

Java-Code:

ServletContext context = request.getServletContext();
context.setAttribute("appName", "My Shop");

JSP:

<jsp:useBean id="config" class="com.example.model.AppConfig" scope="application" />

Expression Language:

${applicationScope.appName}
<!-- oder einfach -->
${appName}

Wann verwenden?

  • Konfiguration: Datenbank-URLs, API-Keys (aber NICHT in Production!)
  • Globale Zähler: Visitor Counter
  • Shared Resources: Connection Pools (sollte aber über JNDI laufen)
  • Cache: Oft verwendete Daten

Beispiel: Visitor Counter

// VisitorCounterListener
@WebListener
public class VisitorCounterListener implements ServletContextListener {
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext context = sce.getServletContext();
        context.setAttribute("visitorCount", 0);
    }
    
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // Cleanup
    }
}
// HomeServlet
@WebServlet("/home")
public class HomeServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, 
                        HttpServletResponse response) 
                        throws ServletException, IOException {
        
        ServletContext context = getServletContext();
        
        synchronized (context) {
            Integer count = (Integer) context.getAttribute("visitorCount");
            count++;
            context.setAttribute("visitorCount", count);
        }
        
        request.getRequestDispatcher("/WEB-INF/views/home.jsp")
               .forward(request, response);
    }
}
<!-- home.jsp -->
<h1>Welcome!</h1>
<p>Total Visitors: ${applicationScope.visitorCount}</p>

<!-- Zähler gilt für ALLE User! -->

Scope Priority & Lookup

Wichtig:

Expression Language sucht in folgender Reihenfolge:

  1. Page Scope
  2. Request Scope
  3. Session Scope
  4. Application Scope

Beispiel:

<%
    pageContext.setAttribute("data", "page");
    request.setAttribute("data", "request");
    session.setAttribute("data", "session");
    application.setAttribute("data", "application");
%>

<!-- Was wird angezeigt? -->
<p>${data}</p>
<!-- Output: "page" (erster gefundener Wert!) -->

<!-- Explizit angeben: -->
<p>${pageScope.data}</p>        <!-- "page" -->
<p>${requestScope.data}</p>     <!-- "request" -->
<p>${sessionScope.data}</p>     <!-- "session" -->
<p>${applicationScope.data}</p> <!-- "application" -->

Best Practice:

Immer explizit angeben, welchen Scope du meinst:

<!-- ✅ GUT: Explizit -->
<p>Username: ${sessionScope.user.username}</p>

<!-- ❌ OKAY, aber unklar: -->
<p>Username: ${user.username}</p>

🟢 GRUNDLAGEN: JSP-Direktiven im Detail

Was sind JSP-Direktiven?

Definition:

Direktiven sind Anweisungen an den JSP-Container, wie er die JSP-Seite verarbeiten soll. Sie werden zur Compile-Zeit ausgeführt, nicht zur Runtime.

Syntax:

<%@ directive attribute="value" attribute="value" ... %>

Die 3 Direktiven:

  1. <%@ page %> – Seiten-Konfiguration
  2. <%@ include %> – Statisches Include (zur Compile-Zeit)
  3. <%@ taglib %> – Tag-Library einbinden

<%@ page %> – Seiten-Direktive

Wichtigste Attribute:

<%@ page 
    contentType="text/html;charset=UTF-8"
    language="java"
    import="java.util.*, com.example.model.*"
    session="true"
    errorPage="/error.jsp"
    isErrorPage="false"
    buffer="8kb"
    autoFlush="true"
    isThreadSafe="true"
    info="My JSP Page"
    pageEncoding="UTF-8"
%>

Wichtige Attribute im Detail:

contentType

<!-- HTML -->
<%@ page contentType="text/html;charset=UTF-8" %>

<!-- JSON -->
<%@ page contentType="application/json;charset=UTF-8" %>

<!-- XML -->
<%@ page contentType="application/xml;charset=UTF-8" %>

<!-- Plain Text -->
<%@ page contentType="text/plain;charset=UTF-8" %>

import

<!-- Einzelne Klasse -->
<%@ page import="com.example.model.User" %>

<!-- Mehrere Klassen (Komma-getrennt) -->
<%@ page import="java.util.List, java.util.ArrayList, com.example.model.*" %>

<!-- Alternative: Mehrere page-Direktiven -->
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="com.example.model.User" %>

session

<!-- Session aktivieren (default: true) -->
<%@ page session="true" %>

<!-- Session deaktivieren (für statische Seiten) -->
<%@ page session="false" %>

<!-- Wenn session="false", dann ${sessionScope} nicht verfügbar! -->

errorPage & isErrorPage

Normale Seite:

<%@ page errorPage="/error.jsp" %>

<h1>My Page</h1>

<%
    // Wenn hier Exception geworfen wird → automatisch zu error.jsp
    int result = 10 / 0; // ArithmeticException!
%>

Error Page:

<%@ page isErrorPage="true" %>
<!DOCTYPE html>
<html>
<head>
    <title>Error</title>
</head>
<body>
    <h1>Oops! Something went wrong.</h1>
    
    <!-- Implizites "exception" Objekt nur verfügbar, wenn isErrorPage="true" -->
    <p>Error: ${exception.message}</p>
    
    <pre>${exception}</pre>
</body>
</html>

Wichtig:

  • errorPage definiert welche Seite bei Fehler aufgerufen wird
  • isErrorPage="true" macht die Seite zur Error-Page (gibt exception Objekt)

buffer & autoFlush

<!-- Buffer-Größe (default: 8kb) -->
<%@ page buffer="16kb" %>

<!-- Auto-Flush (default: true) -->
<%@ page autoFlush="true" %>

<!-- Kein Auto-Flush (Output wird gebuffert, kann zu IllegalStateException führen) -->
<%@ page autoFlush="false" %>

Was bedeutet das?

  • Buffer: Output wird erst gesammelt, dann gesendet
  • autoFlush: Bei vollem Buffer automatisch senden
  • autoFlush=“false“: Exception, wenn Buffer voll (selten gewünscht!)

<%@ include %> – Include-Direktive

Syntax:

<%@ include file="/path/to/file.jsp" %>

Was macht es?

Inkludiert die Datei zur Compile-Zeit (statisches Include). Die Datei wird in die JSP eingefügt, bevor sie zu einem Servlet kompiliert wird.

Beispiel:

header.jsp:

<header>
    <h1>My Website</h1>
    <nav>
        <a href="/">Home</a>
        <a href="/products">Products</a>
        <a href="/contact">Contact</a>
    </nav>
</header>

footer.jsp:

<footer>
    <p>&copy; 2025 My Website. All rights reserved.</p>
</footer>

index.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
    <title>Home</title>
</head>
<body>
    <%@ include file="/WEB-INF/includes/header.jsp" %>
    
    <main>
        <h2>Welcome to our shop!</h2>
    </main>
    
    <%@ include file="/WEB-INF/includes/footer.jsp" %>
</body>
</html>

Wichtig:

  • Compile-Zeit: Datei wird eingefügt, bevor JSP kompiliert wird
  • Performance: Nur einmal kompiliert
  • Änderungen: Bei Änderung von header.jsp muss index.jsp neu kompiliert werden!

Morgen (Tag 7) lernst du den Unterschied zwischen:

  • <%@ include file="..." %> (statisches Include zur Compile-Zeit)
  • <jsp:include page="..." /> (dynamisches Include zur Runtime)

<%@ taglib %> – Tag-Library-Direktive

Syntax:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

Was macht es?

Bindet eine Tag-Library ein. Die wichtigste ist JSTL (Java Standard Tag Library).

Beispiel: JSTL Core Tags

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE html>
<html>
<head>
    <title>Products</title>
</head>
<body>
    <h1>Our Products</h1>
    
    <!-- JSTL forEach -->
    <c:forEach var="product" items="${products}">
        <div>
            <h2>${product.name}</h2>
            <p>Price: ${product.price} €</p>
        </div>
    </c:forEach>
</body>
</html>

Wichtig:

  • JSTL lernst du morgen (Tag 8) im Detail!
  • Für heute reicht: <%@ taglib %> bindet Tag-Libraries ein

🟡 PROFESSIONALS: JavaBeans in echten Projekten

Best Practices für JavaBeans

1. Immer Serializable implementieren

// ✅ GUT
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    // ...
}

Warum?

  • Session-Serialization (bei Server-Restart)
  • Clustering (Session-Replikation zwischen Servern)
  • Caching

2. No-arg Constructor MUSS vorhanden sein

// ✅ GUT
public class Product {
    private int id;
    private String name;
    
    // No-arg Constructor
    public Product() {}
    
    // Constructor mit Parametern
    public Product(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

// ❌ FEHLER: Kein no-arg Constructor!
public class Product {
    private int id;
    private String name;
    
    // Nur dieser Constructor → <jsp:useBean> schlägt fehl!
    public Product(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

3. Getter/Setter-Naming-Konventionen

// ✅ GUT: Standard Bean-Konvention
private String username;

public String getUsername() {
    return username;
}

public void setUsername(String username) {
    this.username = username;
}

// ❌ FALSCH: EL findet Property nicht!
private String username;

public String getUserName() { // Capital N!
    return username;
}

EL-Regel:

  • Property: username
  • Getter: getUsername() (lowercase u, dann camelCase)
  • EL-Zugriff: ${user.username} (ruft getUsername() auf!)

4. Boolean Properties

// ✅ GUT: is-Prefix für boolean
private boolean active;

public boolean isActive() {
    return active;
}

public void setActive(boolean active) {
    this.active = active;
}

// EL-Zugriff:
// ${user.active}  → ruft isActive() auf!

5. toString() überschreiben

@Override
public String toString() {
    return "User{" +
            "username='" + username + '\'' +
            ", email='" + email + '\'' +
            ", role='" + role + '\'' +
            '}';
}

Warum?

  • Debugging (Logging)
  • Fehleranalyse
  • Entwickler-Freundlichkeit

Bean Validation mit JSR-380

Dependency in pom.xml:

<dependency>
    <groupId>jakarta.validation</groupId>
    <artifactId>jakarta.validation-api</artifactId>
    <version>3.0.2</version>
</dependency>

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>8.0.0.Final</version>
</dependency>

Validiertes Bean:

package com.example.model;

import jakarta.validation.constraints.*;
import java.io.Serializable;

public class User implements Serializable {
    
    @NotBlank(message = "Username is required")
    @Size(min = 3, max = 20, message = "Username must be 3-20 characters")
    private String username;
    
    @NotBlank(message = "Email is required")
    @Email(message = "Invalid email format")
    private String email;
    
    @NotNull(message = "Role is required")
    private String role;
    
    @Min(value = 18, message = "You must be at least 18 years old")
    private int age;
    
    // No-arg Constructor
    public User() {}
    
    // Getter/Setter
    // ...
}

Validation in Servlet:

import jakarta.validation.*;
import java.util.Set;

@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
    
    private Validator validator;
    
    @Override
    public void init() throws ServletException {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }
    
    @Override
    protected void doPost(HttpServletRequest request, 
                         HttpServletResponse response) 
                         throws ServletException, IOException {
        
        // Bean aus Request-Parametern erstellen
        User user = new User();
        user.setUsername(request.getParameter("username"));
        user.setEmail(request.getParameter("email"));
        user.setRole(request.getParameter("role"));
        
        try {
            user.setAge(Integer.parseInt(request.getParameter("age")));
        } catch (NumberFormatException e) {
            user.setAge(0);
        }
        
        // Validieren
        Set<ConstraintViolation<User>> violations = validator.validate(user);
        
        if (violations.isEmpty()) {
            // ✅ Validation passed
            HttpSession session = request.getSession();
            session.setAttribute("user", user);
            response.sendRedirect("dashboard.jsp");
        } else {
            // ❌ Validation failed
            request.setAttribute("user", user);
            request.setAttribute("errors", violations);
            request.getRequestDispatcher("/WEB-INF/views/register.jsp")
                   .forward(request, response);
        }
    }
}

JSP mit Fehleranzeige:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE html>
<html>
<head>
    <title>Register</title>
    <style>
        .error { color: red; }
    </style>
</head>
<body>
    <h1>Register</h1>
    
    <!-- Fehler anzeigen -->
    <c:if test="${not empty errors}">
        <div class="error">
            <h3>Please fix the following errors:</h3>
            <ul>
                <c:forEach var="error" items="${errors}">
                    <li>${error.message}</li>
                </c:forEach>
            </ul>
        </div>
    </c:if>
    
    <form action="register" method="post">
        <label>Username:</label>
        <input type="text" name="username" value="${user.username}" required /><br/>
        
        <label>Email:</label>
        <input type="email" name="email" value="${user.email}" required /><br/>
        
        <label>Age:</label>
        <input type="number" name="age" value="${user.age}" required /><br/>
        
        <label>Role:</label>
        <select name="role">
            <option value="User" ${user.role == 'User' ? 'selected' : ''}>User</option>
            <option value="Developer" ${user.role == 'Developer' ? 'selected' : ''}>Developer</option>
        </select><br/>
        
        <button type="submit">Register</button>
    </form>
</body>
</html>

Scope Best Practices

1. Verwende Request-Scope für MVC

// ✅ GUT: Controller → View mit Request-Scope
@WebServlet("/products")
public class ProductServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, 
                        HttpServletResponse response) 
                        throws ServletException, IOException {
        
        List<Product> products = productService.findAll();
        request.setAttribute("products", products); // Request-Scope!
        
        request.getRequestDispatcher("/WEB-INF/views/products.jsp")
               .forward(request, response);
    }
}

// ❌ SCHLECHT: Session-Scope für einmalige Daten
session.setAttribute("products", products); // Warum Session? Macht keinen Sinn!

2. Session-Scope nur für User-State

// ✅ GUT: Login-Daten in Session
User user = authenticate(username, password);
session.setAttribute("currentUser", user);

// ✅ GUT: Warenkorb in Session
ShoppingCart cart = new ShoppingCart();
session.setAttribute("cart", cart);

// ❌ SCHLECHT: Suchergebnisse in Session (sollte Request sein!)
session.setAttribute("searchResults", results);

3. Application-Scope sparsam verwenden

// ✅ GUT: Read-Only Configuration
ServletContext context = getServletContext();
context.setAttribute("appVersion", "1.0.0");
context.setAttribute("supportEmail", "support@java-developer.online");

// ❌ SCHLECHT: User-spezifische Daten
context.setAttribute("lastUser", user); // Alle User sehen das!

// ❌ SCHLECHT: Mutable Shared State ohne Synchronization
List<String> logs = new ArrayList<>();
context.setAttribute("logs", logs); // Race Conditions!

4. Explizite Scope-Angabe in EL

<!-- ✅ GUT: Explizit -->
<p>Username: ${sessionScope.currentUser.username}</p>
<p>Products: ${requestScope.products.size()}</p>

<!-- ❌ OKAY, aber unklar: -->
<p>Username: ${currentUser.username}</p>

Häufige Probleme & Lösungen

Problem 1: Bean nicht gefunden

Fehler:

javax.el.PropertyNotFoundException: Property 'username' not found on type 'com.example.model.User'

Lösung:

// Prüfe: Ist der Getter korrekt benannt?

// ❌ FALSCH:
public String getUserName() { // Capital N!
    return username;
}

// ✅ RICHTIG:
public String getUsername() {
    return username;
}

Problem 2: Bean hat keine Werte

Fehler:

<!-- Zeigt nichts an -->
<p>Username: ${user.username}</p>

Lösung:

Prüfe, ob das Bean gesetzt wurde:

// Servlet
User user = new User();
user.setUsername("nova");
request.setAttribute("user", user); // Wurde das gemacht?

request.getRequestDispatcher("/view.jsp").forward(request, response);

Problem 3: <jsp:useBean> erstellt neues Bean statt existierendes zu verwenden

Fehler:

<!-- page1.jsp -->
<jsp:useBean id="user" class="com.example.model.User" scope="session" />
<jsp:setProperty name="user" property="username" value="nova" />

<!-- page2.jsp -->
<jsp:useBean id="user" class="com.example.model.User" scope="session" />
<p>Username: ${user.username}</p> <!-- Zeigt nichts an! -->

Lösung:

Prüfe, ob der gleiche id-Name und gleiche scope verwendet werden:

<!-- page1.jsp -->
<jsp:useBean id="currentUser" class="com.example.model.User" scope="session" />

<!-- page2.jsp -->
<jsp:useBean id="currentUser" class="com.example.model.User" scope="session" />
<!-- Jetzt klappt's! -->

Problem 4: Property-Zugriff mit . in Map-Keys

Fehler:

<!-- Map mit Key "user.name" -->
<%
    Map<String, String> data = new HashMap<>();
    data.put("user.name", "nova");
    request.setAttribute("data", data);
%>

<!-- ❌ Funktioniert nicht: -->
<p>${data.user.name}</p> <!-- EL denkt, "data" hat Property "user" mit Sub-Property "name" -->

Lösung:

Verwende []-Notation:

<!-- ✅ Funktioniert: -->
<p>${data['user.name']}</p>

🔵 BONUS: Fortgeschrittene Bean-Techniken

Achtung: Dieser Teil ist optional! Wenn du müde bist, skip ihn und komm später zurück. Die Grundlagen + Professionals sind das Wichtigste! 👍

Custom Converter für <jsp:setProperty>

Problem:

<jsp:setProperty> kann keine komplexen Typen konvertieren:

public class Product {
    private LocalDate releaseDate; // Wie aus String konvertieren?
    
    public void setReleaseDate(LocalDate releaseDate) {
        this.releaseDate = releaseDate;
    }
}

Lösung 1: String-Parameter im Bean

public class Product {
    private LocalDate releaseDate;
    
    // Setter für String
    public void setReleaseDateString(String dateString) {
        this.releaseDate = LocalDate.parse(dateString);
    }
    
    // Getter für LocalDate
    public LocalDate getReleaseDate() {
        return releaseDate;
    }
}
<!-- Formular sendet: releaseDate=2025-10-30 -->
<jsp:setProperty name="product" property="releaseDateString" param="releaseDate" />

Lösung 2: PropertyEditor (veraltet, aber funktioniert)

Erstelle src/main/java/com/example/editor/LocalDateEditor.java:

package com.example.editor;

import java.beans.PropertyEditorSupport;
import java.time.LocalDate;

public class LocalDateEditor extends PropertyEditorSupport {
    
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        setValue(LocalDate.parse(text));
    }
    
    @Override
    public String getAsText() {
        LocalDate date = (LocalDate) getValue();
        return date != null ? date.toString() : "";
    }
}

Registriere im Servlet:

import java.beans.PropertyEditorManager;

@WebServlet("/product")
public class ProductServlet extends HttpServlet {
    
    static {
        PropertyEditorManager.registerEditor(
            LocalDate.class, 
            LocalDateEditor.class
        );
    }
    
    // ...
}

Nested Beans

Problem:

Ein Bean enthält andere Beans:

public class Order {
    private User user;
    private Product product;
    private int quantity;
    
    // Getter/Setter
}

public class User {
    private String username;
    // Getter/Setter
}

public class Product {
    private String name;
    // Getter/Setter
}

Zugriff in JSP:

<!-- order ist im Request-Scope -->
<p>Customer: ${order.user.username}</p>
<p>Product: ${order.product.name}</p>
<p>Quantity: ${order.quantity}</p>

<!-- EL-Aufruf-Kette:
     order.getUser().getUsername()
     order.getProduct().getName()
     order.getQuantity()
-->

Bean-Cloning für Immutability

Problem:

Beans in Session können von mehreren Threads gleichzeitig geändert werden.

Lösung: Defensive Copying

public class User implements Serializable, Cloneable {
    private String username;
    private String email;
    
    @Override
    public User clone() {
        try {
            return (User) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError(); // Should never happen
        }
    }
    
    // Getter/Setter
}

Im Servlet:

// User aus Session holen und clonen
User sessionUser = (User) session.getAttribute("user");
User editUser = sessionUser.clone(); // Clone für Bearbeitung

// editUser in Request-Scope für Formular
request.setAttribute("editUser", editUser);

Weiterführende Ressourcen

JavaBeans Spec:

JSP Actions:

Bean Validation:

Books:

  • „Head First Servlets and JSP“ – Kapitel 8 (JavaBeans)
  • „Pro JSP 2“ – Kapitel 4 (Standard Actions)

💬 Real Talk: Mittagspause-Gespräch

Java Fleet Büro, 13:00 Uhr. Nova, Elyndra und Kat sitzen in der Lounge, Kat hat ihr Laptop offen und debuggt einen Issue.


Nova: „Elyndra, ehrliche Frage: Warum brauchen wir überhaupt JavaBeans? Ich könnte doch einfach alles mit Maps machen?“

Elyndra: „Könnte man. Aber stell dir vor: Du hast 50 Properties in einem User-Objekt. Mit einer Map ist das ein Albtraum – kein Typ-Check, keine Auto-Completion, jeder Tippfehler fällt erst zur Runtime auf.“

Kat (zwischendurch): „Und dann refactorst du ‚email‘ zu ‚emailAddress‘ – viel Spaß, alle 200 Stellen zu finden, wo du map.get("email") geschrieben hast. Bei Beans macht die IDE das automatisch.“

Nova: „Okay, das macht Sinn. Aber was ist mit den Actions? <jsp:useBean> sieht so… oldschool aus. Niemand nutzt das doch noch?“

Elyndra: „Stimmt, in modernen Projekten mit Spring Boot siehst du das selten. Aber das Konzept ist dasselbe – Spring nutzt auch Beans, nur versteckt es die Mechanik hinter Dependency Injection.“

Nova: „Also lerne ich hier ‚Legacy-Zeug‘, das keiner mehr braucht?“

Elyndra (lacht): „Nein, du lernst die Grundlagen. Wenn du verstehst, wie JavaBeans und Scopes funktionieren, verstehst du auch, was Spring Boot unter der Haube macht. Und in Enterprise-Projekten, die seit 15 Jahren laufen? Da siehst du genau diesen Code.“

Kat: „Real talk: Ich habe letzten Monat an einem Banking-System gearbeitet – 2008 gebaut, läuft immer noch. Überall <jsp:useBean> und Session-Scopes. Ohne diese Basics hätte ich null Chance gehabt, das zu verstehen.“

Nova: „Okay, okay, ich bin überzeugt. Lowkey war ich nur genervt, weil ich dachte, ich lerne unnötigen Kram.“

Elyndra: „Das Gute ist: Morgen lernst du JSTL – das ist moderner und macht deinen View-Code viel sauberer. Actions sind die Vorstufe. Baby Steps, weißt du?“

Nova (grinst): „Alright, I’m in. Lass uns weitermachen!“


✅ Checkpoint: Hast du es verstanden?

Bevor du weitermachst, teste dich selbst:

Quiz:

Frage 1: Was sind die 4 Pflicht-Konventionen für JavaBeans?

Frage 2: Was macht <jsp:useBean id="user" class="com.example.model.User" scope="session" />?

Frage 3: Erkläre den Unterschied zwischen den 4 Scopes (page, request, session, application). Wann verwendest du welchen?

Frage 4: Was ist der Unterschied zwischen <jsp:setProperty name="user" property="username" value="nova" /> und <jsp:setProperty name="user" property="*" />?

Frage 5: Warum solltest du in JSPs besser ${user.username} statt <jsp:getProperty name="user" property="username" /> verwenden?


Mini-Challenge:

Aufgabe: Erstelle eine Shopping-Cart-Anwendung mit JavaBeans und Scopes.

Anforderungen:

  1. Erstelle ein Product-Bean mit: id, name, price
  2. Erstelle ein CartItem-Bean mit: product, quantity
  3. Erstelle ein ShoppingCart-Bean mit: List<CartItem> items, getTotalPrice()
  4. Erstelle ein Servlet /products, das eine Produktliste anzeigt
  5. Erstelle ein Servlet /add-to-cart, das ein Produkt zum Warenkorb hinzufügt (Session!)
  6. Erstelle eine JSP /cart.jsp, die den Warenkorb anzeigt mit Gesamtpreis
  7. Verwende Expression Language für alle Views
  8. Nutze Session-Scope für den Warenkorb

Bonus:

  • „Remove from Cart“-Funktion
  • Quantity ändern
  • „Empty Cart“-Button

Hinweise:

  1. ShoppingCart sollte eine addItem(Product, int quantity) Methode haben
  2. ShoppingCart sollte in Session gespeichert werden
  3. Produkte kommen aus dem Request-Scope (vom Servlet)

Lösung:
Die Lösung zu dieser Challenge findest du am Anfang von Tag 7 als Kurzwiederholung! 🚀

Alternativ kannst du die Musterlösung im GitHub-Projekt checken: Java Web Basic – GitHub


Geschafft? 🎉
Dann bist du bereit für die FAQ-Sektion!


❓ Häufig gestellte Fragen

Frage 1: Warum funktioniert ${user.username} nicht, obwohl ich user.setUsername("nova") im Servlet aufgerufen habe?

Stelle sicher, dass du das Bean auch im richtigen Scope gespeichert hast:

// ❌ FALSCH: Bean nur lokal erstellt
User user = new User();
user.setUsername("nova");
// Vergessen: request.setAttribute!

// ✅ RICHTIG:
User user = new User();
user.setUsername("nova");
request.setAttribute("user", user); // Wichtig!

Und in der JSP muss der gleiche Name verwendet werden:

<!-- Servlet: request.setAttribute("user", user); -->
<p>${user.username}</p> <!-- ✅ Klappt -->

<!-- Servlet: request.setAttribute("currentUser", user); -->
<p>${user.username}</p> <!-- ❌ user ist undefined! -->
<p>${currentUser.username}</p> <!-- ✅ Klappt -->

Frage 2: Was ist der Unterschied zwischen <%@ include %> und <jsp:include>?

Kurz gesagt:

  • <%@ include file="..." %>: Statisches Include zur Compile-Zeit
  • <jsp:include page="..." />: Dynamisches Include zur Runtime

Morgen (Tag 7) lernst du den Unterschied im Detail! Stay tuned! 🔥


Frage 3: Kann ich JavaBeans auch ohne Servlet direkt in JSP erstellen?

Ja, mit <jsp:useBean>:

<!-- Erstellt neues Bean, falls nicht vorhanden -->
<jsp:useBean id="user" class="com.example.model.User" scope="request" />

<!-- Properties setzen -->
<jsp:setProperty name="user" property="username" value="nova" />
<jsp:setProperty name="user" property="email" value="nova@java-developer.online" />

<!-- Anzeigen -->
<p>Username: ${user.username}</p>

Aber: Das ist schlechte Architektur! JSPs sollten nur View sein, nicht Controller. Beans sollten im Servlet erstellt und über Request/Session weitergegeben werden (MVC/Model 2).


Frage 4: Wie lösche ich ein Bean aus einem Scope?

// Request-Scope
request.removeAttribute("user");

// Session-Scope
session.removeAttribute("user");

// Application-Scope
getServletContext().removeAttribute("config");

In JSP mit JSTL (lernst du Tag 8):

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<c:remove var="user" scope="session" />

Frage 5: Warum sollte ich Serializable implementieren?

  1. Session-Persistenz: Bei Server-Restart kann die Session gespeichert und wiederhergestellt werden
  2. Clustering: In Server-Clustern werden Session-Objekte zwischen Servern repliziert
  3. Caching: Frameworks wie Hibernate cachen Objekte

Ohne Serializable:

public class User {
    private String username;
    // Kein "implements Serializable"
}

// Im Servlet:
session.setAttribute("user", user);
// → Bei Clustering: NotSerializableException!

Frage 6: Kann ich Constructor-Parameter mit <jsp:useBean> übergeben?

Nein! <jsp:useBean> ruft immer den no-arg Constructor auf.

// ❌ Geht nicht:
<jsp:useBean id="user" class="com.example.model.User">
    <jsp:param name="username" value="nova" /> <!-- Nicht unterstützt! -->
</jsp:useBean>

// ✅ Mach das stattdessen:
<jsp:useBean id="user" class="com.example.model.User" scope="request" />
<jsp:setProperty name="user" property="username" value="nova" />

Oder besser: Erstelle das Bean im Servlet mit Constructor-Parametern und gib es weiter!


Frage 7: Bernd meinte mal, Session-Scope wäre „evil“ und sollte vermieden werden. Hat er recht?

Lowkey ja, aber es kommt drauf an.

Sessions sind problematisch bei:

  • Horizontal Scaling (mehr Server = Session-Replikation nötig)
  • Microservices (Sessions sind Stateful = Anti-Pattern)
  • RESTful APIs (sollten Stateless sein)

Sessions sind sinnvoll bei:

  • Traditionellen Monolithen (wie unser Java Web Basic Kurs)
  • User-Login (authenticated state)
  • Shopping Carts
  • Wizard-Forms (Multi-Step)

Real talk: In modernen Cloud-Native Apps nutzt man oft:

  • Stateless JWT-Tokens statt Sessions
  • Redis/Memcached für Shared State
  • Client-Side Storage (Cookies, Local Storage)

Aber für klassische Java Web Apps (wie im Bank-Beispiel von Kat) sind Sessions Standard. Don’t overthink it – learn the basics first, optimize later! 🚀


📚 Quiz-Lösungen

Hier sind die Antworten zum Quiz von oben:


Frage 1: Was sind die 4 Pflicht-Konventionen für JavaBeans?

Antwort:

  1. Public class – Die Klasse muss öffentlich sein (public class User)
  2. No-arg Constructor – Ein Konstruktor ohne Parameter muss vorhanden sein (public User() {})
  3. Private properties – Alle Felder sind privat (private String username;)
  4. Public getter/setter – Zugriff über öffentliche getter/setter-Methoden (public String getUsername(), public void setUsername(String username))

Optional, aber empfohlen: 5. Serializable – Implementierung von java.io.Serializable


Frage 2: Was macht <jsp:useBean id="user" class="com.example.model.User" scope="session" />?

Antwort:

Diese Action macht folgendes:

  1. Sucht im Session-Scope nach einem Attribut mit dem Namen "user"
  2. Falls gefunden: Verwendet das existierende Bean (keine neue Instanz!)
  3. Falls nicht gefunden:
    • Erstellt neue Instanz mit new com.example.model.User() (no-arg Constructor!)
    • Speichert sie im Session-Scope unter dem Namen "user"
    • Macht sie als Variable user in dieser JSP verfügbar

Entspricht diesem Java-Code:

User user = (User) session.getAttribute("user");
if (user == null) {
    user = new User();
    session.setAttribute("user", user);
}

Frage 3: Erkläre den Unterschied zwischen den 4 Scopes (page, request, session, application). Wann verwendest du welchen?

Antwort:

ScopeLebensdauerVerwendung
pageNur diese JSP-SeiteTemporäre Variablen, Loop-Counter, Zwischenergebnisse nur für diese Seite
requestEin HTTP-Request (inkl. Forward/Include)MVC Pattern: Controller setzt Daten, View zeigt sie an. Suchergebnisse, Formular-Daten. Am häufigsten verwendet!
sessionEine User-Session (~30 Min Inaktivität)User-Login, Warenkorb, User-Einstellungen, Multi-Step-Forms
applicationGesamte Webapp-LaufzeitKonfiguration, globale Zähler, Shared Resources (aber vorsichtig – alle User sehen das!)

Best Practice:

  • Standard: Request-Scope (stateless!)
  • Nur wenn nötig: Session-Scope (stateful)
  • Sehr selten: Application-Scope (global shared state)
  • Fast nie: Page-Scope (zu begrenzt)

Frage 4: Was ist der Unterschied zwischen <jsp:setProperty name="user" property="username" value="nova" /> und <jsp:setProperty name="user" property="*" />?

Antwort:

Mit value="nova":

<jsp:setProperty name="user" property="username" value="nova" />
  • Setzt die Property username manuell auf den Wert "nova"
  • Entspricht: user.setUsername("nova");
  • Unabhängig von Request-Parametern

Mit property="*":

<jsp:setProperty name="user" property="*" />
  • Setzt ALLE Properties automatisch aus Request-Parametern
  • JSP schaut sich alle Parameter an (?username=nova&email=...) und versucht, sie mit Bean-Properties zu matchen
  • Request-Parameter username → ruft setUsername(...) auf
  • Request-Parameter email → ruft setEmail(...) auf
  • Fehlende oder nicht-matchende Parameter werden ignoriert
  • Entspricht: if (request.getParameter("username") != null) { user.setUsername(request.getParameter("username"));}if (request.getParameter("email") != null) { user.setEmail(request.getParameter("email"));}// usw. für alle Properties

Use-Case:

  • value="...": Hardcoded-Werte, Default-Werte
  • property="*": Formulare, Auto-Binding von Request-Parametern

Frage 5: Warum solltest du in JSPs besser ${user.username} statt <jsp:getProperty name="user" property="username" /> verwenden?

Antwort:

Expression Language (${user.username}) ist besser, weil:

  1. Kürzer:
    • EL: ${user.username}
    • Action: <jsp:getProperty name="user" property="username" />
  2. Lesbarer: EL liest sich wie normaler Property-Zugriff
  3. Flexibler: EL kann verschachtelt werden: ${order.user.address.city} Mit Actions müsstest du mehrere Tags schachteln (unmöglich)
  4. Null-Safe: EL zeigt nichts an, wenn Property null ist ${user.username} <!-- Zeigt nichts, wenn user oder username null --> Action würde Exception werfen!
  5. Mehr Features: EL kann Operatoren, Funktionen, Collections: ${user.age > 18 ? 'Adult' : 'Minor'} ${products.size()} ${user.username.toUpperCase()}

Fazit:

<jsp:getProperty> ist Legacy aus JSP 1.x-Zeiten (vor 2001). Expression Language wurde in JSP 2.0 (2003) eingeführt und ist der moderne Standard.

Use-Case für <jsp:getProperty>: Keine! Nutze immer EL! 🚀


🎉 Tag 6 geschafft!

Slay! Du hast es geschafft! 🚀

Real talk: JavaBeans und Scopes sind das Fundament für saubere Java Webanwendungen. Das war kein leichter Tag – es gab viel Theorie, viele Konzepte. Aber du hast durchgezogen. Das ist huge.

Das hast du heute gerockt:

  • ✅ JavaBeans verstanden und selbst erstellt
  • ✅ JSP Standard Actions kennengelernt (<jsp:useBean>, <jsp:setProperty>)
  • ✅ Die 4 Scopes gemeistert (page, request, session, application)
  • ✅ JSP-Direktiven im Detail gelernt
  • ✅ Best Practices für Production-Code

Du kannst jetzt:

  • Typsichere, wiederverwendbare Datencontainer erstellen
  • Beans in verschiedenen Scopes verwalten
  • Saubere MVC-Architekturen mit Request/Session-Scopes bauen
  • Komplexe Formulare mit Auto-Binding implementieren

Honestly? Du bist jetzt weiter als 80% der Developer, die „JSP“ nur vom Hörensagen kennen. Ngl, du hast heute die Grundlage für alles gelegt, was noch kommt. 💪

Main Character Energy: Unlocked! ✨


Wie geht’s weiter?

Morgen (Tag 7): Include-Action vs Include-Direktive

Was dich erwartet:

  • Unterschied zwischen <%@ include %> und <jsp:include> verstehen
  • Compile-Zeit vs. Runtime Includes
  • Wann du welches Include verwendest
  • Parameter an Includes übergeben
  • Dynamische Content-Generierung
  • Das wird dein Game-Changer für wiederverwendbare View-Komponenten! 🔥

Brauchst du eine Pause?
Mach sie! JavaBeans und Scopes sind heavy topics. Lass das heute sacken, spiel mit dem Code, bau die Challenge. Komm morgen zurück, wenn du bereit bist. 😊

Tipp für heute Abend:
Bau die Shopping-Cart-Challenge aus! Füge Features hinzu:

  • Mehrere Produkte mit Kategorien
  • Quantity im Cart ändern
  • „Clear Cart“-Button
  • Gesamtpreis mit Mehrwertsteuer

Learning by doing! 🔧


🔧 Troubleshooting

Problem: <jsp:useBean> wirft ClassNotFoundException

Lösung:

<!-- ❌ FALSCH: Relative Paketangabe -->
<jsp:useBean id="user" class="User" scope="request" />

<!-- ✅ RICHTIG: Vollqualifizierter Klassenname -->
<jsp:useBean id="user" class="com.example.model.User" scope="request" />

Prüfe außerdem:

  1. Ist die Klasse kompiliert? (Clean and Build in NetBeans)
  2. Liegt die .class-Datei in WEB-INF/classes/com/example/model/User.class?
  3. Hat die Klasse einen no-arg Constructor?

Problem: Bean hat keine Werte, obwohl ich setProperty verwendet habe

Lösung:

<!-- Reihenfolge ist wichtig! -->

<!-- ❌ FALSCH: setProperty VOR useBean -->
<jsp:setProperty name="user" property="username" value="nova" />
<jsp:useBean id="user" class="com.example.model.User" scope="request" />

<!-- ✅ RICHTIG: useBean VOR setProperty -->
<jsp:useBean id="user" class="com.example.model.User" scope="request" />
<jsp:setProperty name="user" property="username" value="nova" />

Problem: Expression Language zeigt Bean-Property nicht an

Lösung:

Prüfe die Getter-Methode:

// ❌ FALSCH: Groß/Kleinschreibung stimmt nicht
private String userName;

public String getUserName() { // Falsches Naming!
    return userName;
}

// EL: ${user.userName} funktioniert NICHT!

// ✅ RICHTIG:
private String username;

public String getUsername() {
    return username;
}

// EL: ${user.username} funktioniert!

Regel: Property username braucht Getter getUsername() (lowercase u, dann camelCase!)


Problem: Session-Bean ist nach einiger Zeit weg

Grund:

Sessions haben ein Timeout (default: 30 Minuten Inaktivität).

Lösung 1: Timeout erhöhen (in web.xml)

<session-config>
    <session-timeout>60</session-timeout> <!-- 60 Minuten -->
</session-config>

Lösung 2: Session-Timeout in JSP setzen

<%
    session.setMaxInactiveInterval(3600); // 3600 Sekunden = 1 Stunde
%>

Lösung 3: Check, ob Session existiert

<c:choose>
    <c:when test="${not empty sessionScope.user}">
        <p>Welcome, ${user.username}!</p>
    </c:when>
    <c:otherwise>
        <p>Please <a href="login.jsp">login</a>.</p>
    </c:otherwise>
</c:choose>

Problem: property="*" setzt nicht alle Properties

Grund:

Request-Parameter-Namen müssen exakt mit Bean-Property-Namen übereinstimmen.

Lösung:

<!-- Formular -->
<form action="register.jsp" method="post">
    <input type="text" name="username" /> <!-- ✅ -->
    <input type="text" name="userName" /> <!-- ❌ Funktioniert nicht! -->
    <input type="text" name="user_name" /> <!-- ❌ Funktioniert nicht! -->
</form>
// Bean
public class User {
    private String username; // Muss übereinstimmen!
    
    public void setUsername(String username) {
        this.username = username;
    }
}

Regel:

  • Input name="username" → Bean-Property username → Setter setUsername()
  • Case-Sensitive! usernameuserNameuser_name

📚 Resources & Links

Offizielle Dokumentation:

Bean Validation:

Best Practices:

Unsere GitHub-Repos:


💬 Feedback

Wie war Tag 6 für dich?

Was können wir verbessern? Dein Feedback hilft uns, den Kurs besser zu machen!


👋 Bis morgen!

Das war Tag 6 – ein wichtiger Schritt auf deinem Java Web-Journey!

Du hast heute gelernt, wie man Daten sauber und typsicher in Webanwendungen verwaltet. JavaBeans sind überall – selbst wenn du später Spring Boot nutzt, basiert alles auf diesen Konzepten.

Bis morgen! 👋

Elyndra


Java Web Basic – Tag 6 von 10
Teil der Java Fleet Learning-Serie
© 2025 Java Fleet Systems Consulting
Website: java-developer.online

Autor

  • Elyndra Valen

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