JavaServer Faces heute: von JSF 2.x zu Jakarta Faces 4
JavaServer Faces hat den Ruf einer Technik aus der Java-EE-Zeit. In Unternehmen und Behörden ist JSF trotzdem weit verbreitet, und die Spezifikation lebt weiter. Zwischenzeitlich hieß sie Jakarta Server Faces, mit Jakarta EE 10 bekam sie den Namen Jakarta Faces, und die aktuelle Version 4.1 gehört zum Plattform-Release EE 11 vom Juni 2025. Dieser Beitrag geht die Fragen durch, die bei Wartung und Migration auf den Tisch kommen. Als Beispiel dient ein kleiner Onlineshop mit Warenkorb und Bestellprozess.
Was ist JavaServer Faces eigentlich?
JSF ist ein serverseitiges Komponenten-Framework nach dem MVC-Prinzip. Jede Seite existiert auf dem Server als Baum von UI-Komponenten, der sogenannten View. Eine Anfrage durchläuft dabei sechs Phasen, von Restore View bis Render Response. Unterwegs werden die Eingaben konvertiert, validiert und in die Bean geschrieben. Zwischen den Phasen feuert das Framework Events, auf die sich eigene Listener registrieren lassen.
Seit JSF 2.0 ist Facelets die View-Technik. Die Seite für den Warenkorb:
<html xmlns:h="jakarta.faces.html">
<h:form>
<h:inputText id="menge" type="number" value="#{warenkorb.menge}"/>
<h:message for="menge"/>
<h:commandButton value="Aktualisieren" action="#{warenkorb.aktualisieren}"/>
</h:form>
</html>
Das Attribut type an h:inputText gibt es erst ab 4.0. Dahinter steht eine gewöhnliche CDI-Bean:
@Named @ViewScoped
public class Warenkorb implements Serializable {
private int menge;
private List<Position> positionen;
public void aktualisieren() { /* Preis neu berechnen */ }
}
Die Expression Language (#{...}) verbindet Komponenten mit Properties und Methoden der Bean. Eigene Bausteine entstehen als Composite Component aus einer XHTML-Datei. Für die Ausgabe sorgen Renderer, die aus jedem UIComponent Markup erzeugen.
Wie hat sich JSF seit 2004 entwickelt?
Die Versionsgeschichte erklärt, warum alte Projekte oft mehrere Stile mischen.
| Release | Jahr | JSR / Plattform | Wichtigste Neuerung |
|---|---|---|---|
| JSF 1.0 / 1.2 | 2004 / 2006 | JSR 127 / JSR 252 | JavaServer Pages (JSP) als View-Technik |
| JSF 2.0 / 2.1 | 2009 / 2010 | JSR 314 | Facelets, Ajax mit f:ajax, Annotationen |
| JSF 2.2 | 2013 | JSR 344 | Faces Flows, Stateless Views, Resource Library Contracts |
| JSF 2.3 | 2017 | JSR 372 | CDI statt eigener Managed Beans, WebSocket |
| 3.0 | 2020 | EE 9 | Paketwechsel von javax auf jakarta |
| 4.0 | 2022 | EE 10 | JSP und @ManagedBean entfernt, neuer Name |
| 4.1 | 2024 | EE 11 | Java SE 17, Full State Saving deprecated |
An 5.0 für Jakarta EE 12 wird gearbeitet. Die Eclipse Foundation betreut die Spezifikation, seit Oracle Java EE abgegeben hat.

Was ändert sich beim Umstieg auf jakarta.faces?
Die meiste Arbeit macht der Paketwechsel. Ab 3.0 liegen alle API-Klassen unter jakarta.faces statt javax.faces. Mojarra führt intern weiterhin das Package com.sun.faces. Eine Anwendung auf JSF 2.3 braucht diese Schritte:
- Imports von
javax.faces,javax.injectundjavax.enterpriseauf das Präfixjakartaumstellen. - Maven-Abhängigkeiten auf die API-Artefakte von 4.1 tauschen.
@ManagedBeandurch CDI Managed Beans ersetzen, also@Namedplus CDI-Scope. Die nativen Managed Beans gibt es seit 4.0 nicht mehr.- XHTML-Namespaces auf die kurzen URNs umstellen: Aus
xmlns:h="http://xmlns.jcp.org/jsf/html"wirdxmlns:h="jakarta.faces.html". - Context-Parameter in der
web.xmlauf das neue Präfix umbenennen. - Verbliebene JSP-Seiten auf XHTML portieren.
- PrimeFaces, OmniFaces und andere Komponentenbibliotheken in aktuellen Major-Versionen einbinden.
Einen großen Teil davon erledigen OpenRewrite-Rezepte wie „JSF 2.x to Jakarta Faces 4.x“. Von Hand bleiben meist eigene Komponenten und Bibliotheken übrig, die nie portiert wurden. Solche Altbibliotheken bekommen oft keine Sicherheitsupdates mehr. Spätestens bei Compliance-Anforderungen an Softwareprojekte fällt das auf.
Welche Scopes gibt es, und wann nimmt man welchen?
Alle Scopes kommen seit 2.3 aus CDI (Contexts and Dependency Injection) oder aus der Faces-API selbst. Faustregel: so kurz wie möglich.
@RequestScopedlebt eine Anfrage lang, passend für eine Suchmaske.@ViewScopedhält, solange der Nutzer auf derselben Seite bleibt. Für den Warenkorb mit Ajax ist das die richtige Wahl.@FlowScopedgilt für die Dauer eines Flows.@ClientWindowScopedist neu in 4.0 und trennt nach Browser-Tab. Das Prinzip stammt aus DeltaSpike.@SessionScopedreicht für Login-Daten und Spracheinstellung.@ApplicationScopedhält Kataloge und Konfiguration für alle Nutzer.
Custom Scopes, die man unter JSF 2.0 noch über @CustomScoped baute, entstehen heute als CDI-Extension mit einer eigenen Context-Implementierung. Wenn eine Anwendung zu viel Speicher frisst, liegt es meistens an zu vielen oder zu dicken Session-Beans.
Was sind Faces Flows, und braucht man sie noch?
Flows kamen mit JSF 2.2. Ein Flow bündelt mehrere Seiten mit gemeinsamem Zustand, festem Einstieg und definiertem Ausstieg. Im Onlineshop ist das der Bestellprozess mit Adresse, Versand, Zahlung und Bestätigung. Die Seiten liegen unter /bestellung/, der Zustand steckt in einer @FlowScoped("bestellung")-Bean. Definiert wird der Flow entweder über eine XML-Datei oder in Java mit @FlowDefinition und FlowBuilder.
Wir setzen Flows ein, wenn ein Prozess wirklich über mehrere Seiten geht und von verschiedenen Stellen aus startet. Im einfachen Fall, einem Assistenten mit drei Schritten auf einer Seite, reicht eine @ViewScoped-Bean mit einem Schrittzähler. Eine solche Bean lässt sich leichter testen.
Was bringen Stateless Views?
Zwischen zwei Anfragen speichert das Framework den Zustand des Komponentenbaums. Das kostet Speicher und erzeugt die bekannte ViewExpiredException, sobald die Session abgelaufen ist und der Nutzer trotzdem noch ein altes Formular abschickt. Ab 2.2 lässt sich das pro Seite abschalten:
<f:view transient="true">
Für Login-Seiten und öffentliche Suchformulare passt das gut. Der Preis: @ViewScoped funktioniert dort nicht, und Komponenten merken sich zwischen zwei Postbacks nichts. Partial State Saving ist seit 2.0 der Standard, Full State Saving ist in 4.1 deprecated.
Mojarra oder MyFaces: Welche Implementierung ist schneller?
Zwei vollständige Implementierungen stehen zur Wahl. Mojarra ist die kompatible Implementierung der Spezifikation und steckt etwa in GlassFish und WildFly, während TomEE und Open Liberty auf Apache MyFaces setzen. Bis Mojarra 4.1.9 lag MyFaces in Messungen vorn.
Im Juli 2026 veröffentlichte Mojarra-Maintainer Bauke Scholtz (BalusC) einen Vergleich über alle Lifecycle-Phasen auf sechs Servern. Mojarra 4.1.10 brauchte 64 Prozent weniger Zeit als 4.1.9 und lag insgesamt 18 Prozent vor einem Snapshot von MyFaces 4.1.4. Der Autor ist Partei, seine Messmethode hat er offen dokumentiert. Unsere Empfehlung: die Implementierung des Applikationsservers verwenden und nur nach eigenen Messungen wechseln.
Wie passt JSTL in Facelets?
Die JSTL stammt aus der JSP-Welt, Facelets kennt nur einen Teil davon. Aus der Core-Bibliothek sind das c:if, c:forEach, c:choose, c:when, c:otherwise, c:set und c:catch, dazu kommen die fn:-Funktionen. Formatieren mit fmt: geht nicht, dafür gibt es f:convertNumber und f:convertDateTime. Der Namespace heißt ab 4.0 jakarta.tags.core.
Ein Tag wie c:if ist ein Tag-Handler. Er läuft beim Aufbau des Komponentenbaums, ui:repeat und h:dataTable dagegen erst bei der Ausgabe. Daraus entsteht der häufigste Fehler: Fragt ein c:if die Zeilenvariable einer Tabelle ab, sieht es nur null. In Iterationen gehört die Bedingung deshalb ins rendered-Attribut:
<ui:repeat value="#{warenkorb.positionen}" var="p">
<h:outputText value="#{p.name}" rendered="#{p.menge gt 0}"/>
</ui:repeat>
Richtig eingesetzt ist c:forEach, wenn tatsächlich Komponenten entstehen sollen, zum Beispiel dynamische Formularfelder.

Welche Performance-Tipps gelten nach der Migration noch?
Was unter 2.x half, hilft meist weiter. Die Context-Parameter tragen nur ein neues Präfix, jakarta.faces. statt javax.faces.:
PROJECT_STAGEin Produktion aufProductionsetzen.FACELETS_REFRESH_PERIODauf-1stellen, damit der Server nicht laufend nach geänderten Dateien sucht.FACELETS_SKIP_COMMENTSauftrue, sonst landen XHTML-Kommentare in der ausgelieferten Seite.- Die Zahl gespeicherter Views pro Session begrenzen (
numberOfViewsInSessionbei Mojarra,NUMBER_OF_VIEWS_IN_SESSIONbei MyFaces). - Getter ohne Logik schreiben. Ein Getter in einem
rendered-Attribut wird pro Anfrage mehrfach aufgerufen. - Bei Ajax
executeundrendereng fassen und auf@formoder@allverzichten. - Große Tabellen lazy laden, PrimeFaces liefert dafür das
LazyDataModel.
Lohnt sich JavaServer Faces 2026 noch?
Für bestimmte Anwendungen ja. JSF passt, wenn viele Formulare, Validierung und serverseitige Geschäftslogik zusammenkommen und das Team Java kann, aber kein eigenes Frontend-Team hat.
| JSF ist sinnvoll | JSF passt eher nicht |
|---|---|
| Interne Fachanwendungen, Sachbearbeitung | Öffentliche Seiten mit viel Traffic und Caching |
| Bestehende Java-EE-Anwendungen | Offline-fähige Mobile-Apps |
| Teams ohne JavaScript-Spezialisten | SPAs mit separatem Frontend-Team |
| Langlebige Software auf stabiler Spezifikation | Mehrere Clients über eine gemeinsame API |
Für eine bestehende Anwendung auf 2.3 raten wir zur Migration auf 4.1 statt zur Neuentwicklung. Der Umstieg ist überschaubar, und die über Jahre gewachsene Fachlogik bleibt erhalten. Wer ohnehin Geschäftsanwendungen wirtschaftlich modernisieren will, sollte beide Wege mit echten Aufwänden durchrechnen. Was das aktuelle Release technisch verlangt, etwa Java SE 17, steht in der offiziellen Spezifikation von Jakarta Faces 4.1.




1 Comment
[…] belegt, und N+1-Abfragen tauchen erst unter Last auf. Bei serverseitig gerenderten Oberflächen wie JavaServer Faces war das Muster lange verbreitet, heute zeigt sich derselbe Effekt bei JSON-Antworten. Wer […]