Web-Anwendungen unterscheiden sich von anderen Anwendungen hauptsächlich darin, dass sie mit einem Browser verwendet werden können und die Geschäftslogik zentral auf einem Server ausgeführt wird. Durch die klare Trennung der Darstellung vom Datenmodell bleibt das Modell einer Anwendung unabhängig von der Wahl der Darstellung gleich. Schließlich ist einem Artikel-Objekt "egal", ob dessen Attribute auf einer Webseite oder auf der Konsole ausgegeben werden. Daher ist auch davon auszugehen, dass das Modell in vielen Projekten zur Entwicklung von Web-Anwendungen bereits in Form von Java-Klassen vorliegt. Um die Wiederverwendung dieser Klassen zu gewährleisten führt JSF das Konzept der Managed Beans ein.
Bei Beans handet es sich um einfache Klassen des Datenmodells. Sie können mithilfe der Unified Expression Language (UEL) mit der
Darstellungsschicht verknüpft werden. Doch auf ein Java-Objekt kann man nur zugreifen, wenn es zuvor instanziiert und initialisiert
wurde. In einer Java-Anwendung können solche Aufgaben zum Beispiel in der main()-Methode erledigt werden. Aber eine mit JSF
entwickelte Web-Anwendung besitzt keine vergleichbare Methode. Es muss also einen anderen Weg geben, die mit den JSF-Komponenten verknüpften
Objekte zu instanziieren. Die vom Framework angebotene Lösung ist für Web-Entwickler denkbar günstig: JSF übernimmt die
Verantwortung für das Erzeugen der Beans. Deshalb werden sie im JSF-Framework Managed Beans genannt.
Die Kennzeichnung einer bereits existierenden Klasse des Datenmodells als Managed Bean ist einfach, denn es muss nur die Annotation
@ManagedBean hinzugefügt werden. Bei der zweiten Annotation @RequestScoped, welche von der Entwicklungsumgebung
automatisch hinzugefügt wird, handelt es sich um die Gültigkeitsbereiche einer Managed Bean. Sie geben an, wie lange ein Objekt der
Bean existiert bzw. über welchen Zeitraum JSF es bereitstellt. Zum Beispiel könnte ein Objekt nur für die Dauer einer
HTTP-Anfrage gültig sein (@RequestScoped). Das heißt, es wird zu Beginn einer Anfrage erzeugt und nach der Erzeugung der
HTTP-Antwort wieder von JSF gelöscht. Es ist offensichtlich, dass ein solcher Gültigkeitsbereich nicht immer sinnvoll ist.
Beispielsweise darf der Warenkorb eines Online-Shops nicht nach jedem Mausklick neu initialisiert werden. Vielmehr müsste er über eine
gesamte Browser-Sitzung gültig sein (@SessionScoped). Die folgende Tabelle bietet einen Überblick über alle
möglichen Gültigkeitsbereiche sowie deren Zusammenhänge.
| Gültigkeitsbereiche von Managed Beans | ||
|---|---|---|
| Annotation | Beschreibung | Anwendungsbeispiele |
@NoneScoped |
Bean wird nach der Erzeugung wieder gelöscht, sofern keine andere Bean sie nutzt. |
Nützlich, wenn JSF für jeden UEL-Ausdruck eine neue Instanz der Bean erzeugen soll. |
@RequestScoped |
Bean wird nach der Beantwortung der HTTP-Anfrage wieder gelöscht. |
Daten (z.B. Listen der Artikel) sollen bei jedem Klick neu aus der Datenbak geladen werden. |
@ViewScoped |
Bean wird erst gelöscht, wenn der Benutzer zu einer anderen Seite wechselt. |
Daten sollen nicht bei jeder Anfrage neu geladen werden. |
@SessionScoped |
Bean wird erst gelöscht, wenn die Sitzung erlischt (z.B. durch Timeout) |
Der Warenkorb eines Besuchers des Online-Shops. |
@ApplicationScoped |
Bean wird mit Start des Servers erzeugt und nie gelöscht. |
Ein Besucherzähler für die Web-Anwendung. |

Man kann also eine vorhandene Java-Klasse mit nur zwei zusätzlichen Annotationen zu einer Managed Bean umwandeln und direkt in einer
JSF-Komponente verwenden. Aber woher weiß JSF, dass mit artikel ein Objekt der Managed Bean Artikel gemeint ist?
Schließlich ist nirgendwo eine Anweisung wieArtikel artikel = new Artikel();
zu finden. Und wie kann JSF trotz der
Sichtbarkeit private auf das Attribut artikel.name zugreifen?
Damit diese einfache Verwendung von Managed Beans funktioniert, müssen gewisse Konventionen eingehalten werden (man spricht in
solchen Fällen auch vom Paradigma Convention over Configuration oder auch Coding by Convention). Sofern man sich an diese Regeln
hält, muss man also keine Angaben über den Namen des Objekts (Objektbezeichner) oder die exakte Signatur der get- und set-Methoden
machen, um auf das private Attribut zugreifen zu können.
In diesem Fall lauten die Regeln, dass
Wenn man sich nicht an die Konventionen halten kann oder will, können Objektbezeichner und Methodensignaturen auch mittels
attributierter Annotationen selbst definiert werden. Hierzu fügt man den bereits bekannten Annotationen Attribute hinzu. Wenn
man zum Beispiel das Artikel-Objekt in den JSF-Komponenten nicht artikel, sondern einArtikel nennen möchte, kann
man das mit dem Attribut name der Managed Bean-Annotation festlegen:
@ManagedBean(name = "einArtikel")
@RequestScoped
public class Artikel { ... }
Bislang wurden nur JSF-Komponenten zur Anzeige von Informationen verwendet. Um aber die Auswirkungen der unterschiedlichen Gültigkeitsbereiche an dem aktuellen Entwurf der Artikelverwaltung testen zu können, wird noch eine Möglichkeit zur Manipulation der Objekte benötigt. Als Beispiel wird eine Schaltfläche zum Löschen auf der Artikelübersicht gewählt.
Neben einer gewöhnlichen, meist optisch nicht sehr ansprechenden Schaltfläche (h:commandButton), kann hierfür auch
ein Bild verwendet werden, das von einem Hyperlink umschlossen ist. Diese Lösung wird mit der JSF-Komponente h:commandLink
realisiert. Der Vorteil dieser Komponente ist, dass man mit ihrem action-Attribut eine Java-Methode aufrufen kann. Sie wird
aufgerufen, sobald der Besucher der Webseite auf den Link klickt. Weil die Aufgabe der Schaltfläche darin besteht, den Artikel zu der
entsprechenden Zeile zu löschen, bietet sich an, unmittelbar die remove-Methode der Artikelliste in das
action-Attribut einzutragen. Als Parameter der Methode wird das Stellvertreter-Objekt (artikel) der Tabelle
verwendet:
<h:commandLink action="#{shop.sortiment.remove(artikel)}"/>
Hierbei handelt es sich nicht um einen ganz normalen Hyperlink, der eine einfache HTTP-Anfrage mit einer vorab bekannten URL auslöst.
Solche komplexeren HTTP-Anfragen können nur im Zusammenhang mit einem Formular gesendet werden. Bezogen auf JSF-Webanwendungen bedeutet
das, dass die h:commandLink-Komponente von einer h:form-Komponente umschlossen werden muss. Zusammen mit
dem eingebetteten Bild für die Schaltfläche sieht die neue Spalte nun so aus:
Zum Testen startet man das Projekt über die Entwicklungsumgebung, wartet bis die Seite im Browser geladen wurde und klickt auf eine der Schaltflächen. Die Seite wird neu geladen und der entsprechende Artikel ist aus der Liste verschwunden. Dabei tritt die Warnmeldung auf, dass es zu der Aktion noch keine Navigationsregel gibt.
Ein paar kurze Experimente sollen zeigen, wie sich die unterschiedlichen Gültigkeitsbereiche auf das Verhalten der Web-Anwendung
auswirken. Als Gegenstand der Untersuchung wird die Shop-Klasse ausgewählt, die von der Entwicklungsumgebung standardmäßig mit dem
Gültigkeitsbereich @RequestScoped erstellt wurde. Man würde daher erwarten, dass die Veränderungen an dem
Sortiment nach jeder neuen Anfrage wieder auf den Ursprungszustand zurückgesetzt werden. Um das zu testen, muss man allerdings wissen,
dass es mit modernen Browsern manchmal nicht ganz einfach ist, eine wirklich neue Anfrage zu einer Internetseite abzusetzen. Das ist immer dann
der Fall, wenn die Seite zuvor durch eine Aktion in einem Formular (hier: das Löschen eines Artikels aus der Liste) anhand von Parametern
dynamisch erzeugt wurde. Die Browser erkennen diesen Zusammenhang meist beim Aktualisieren der Seite (hervorgerufen z.B. durch die Taste "F5")
und fragen den Benutzer, ob er die Aktion wiederholen möchte. Das löst aber nicht eine neue Anfrage aus, sondern führt dazu, dass
dieselbe Seite erneut angezeigt wird. Um stattdessen tatsächlich eine neue HTTP-Anfrage zu starten, muss man mit der Maus in das
Adressfeld klicken und die Eingabetaste drücken.
Man kann nun beobachten, dass durch das Abschicken der neuen Anfrage ein neues Shop-Objekt von JSF erzeugt und mit den Initialwerten gefüllt wurde. Deswegen werden wieder alle Artikel angezeigt. Eine ähnliche Wirkung wird erzielt, wenn man mehr als einen Artikel zu löschen versucht: Vor jedem Löschvorgang wird das Sortiment wieder zurückgesetzt, sodass immer nur der zuletzt gelöschte Artikel fehlt.
Selbstverständlich ist das nicht das gewünschte Verhalten. Man möchte vielmehr, dass die Artikel dauerhaft aus dem Sortiment
entfernt werden und so starten wir einen neuen Test mit dem Gültigkeitsbereich @SessionScoped. Nun lassen sich die Artikel
nacheinander löschen. Es wird also nict mehr nach jeder Anfrage oder beim Aktualisieren der Seite das Shop-Objekt auf den
Ursprungszustand zurückgesetzt. Bei einem Aufruf der Seite durch einen anderen Browser sind jedoch wieder alle Artikel vorhanden. Das
liegt daran, dass ein zweiter Browser eine eigene Sitzung startet und aufgrund des neuen Gültigkeitsbereiches das Shop-Objekt nunmehr an
eine solche Sitzung gebunden ist.
Im letzten Experiment wenden wir den Gültigkeitsbereich @ApplicationScoped auf die Managed Bean an. Die im Shop verwaltete
Artikelliste soll unabhängig von einzelnen Anfragen, HTML-Seiten oder Browser-Sitzungen existieren. Ein Test zeigt sofort, dass auch mit
unterschiedlichen Browser-Sitzungen und sogar von unterschiedlichen Geräten aus trotzdem auf dasselbe Shop-Objekt zugegriffen wird.