<jsptutorial />

Beans in JSPs


Was sind Beans

In der Java-Welt gibt es zwei Arten von Beans. Die normalen JavaBeans und die Enterprise JavaBeans (EJBs). In diesem Kapitel geht es ausschließlich um die "normalen" JavaBeans, im Folgenden zumeist nur noch Beans genannt. Enterprise JavaBeans haben mit diesen außer dem Namen und der Komponenten-Idee nichts gemeinsam. Sie werden im Rahmen dieses Tutorials im Kapitel "Andere J2EE-Technologien" ausführlich behandelt.
JavaBeans waren ursprünglich gedacht, um insbesondere grafische Komponenten zu ermöglichen. Die Idee dahinter war, eine Namens-Konvention zu schaffen, so dass Tool-Hersteller mittels Introspektion und Reflection1 die Eigenschaften der Komponente herausfinden und diese dann grafisch bearbeitet können (link). So sollte es auch in Java möglich sein, einen Markt für Komponenten zu schaffen, wie er in der Windows-Welt schon länger existiert. Es gibt in der Tat einige Hersteller, die hier spezielle Komponenten liefern, insbesondere Komponenten zur visuellen Repräsentation geographischer Informationen, oder Reporting-Komponenten. Da Java am Desktop aber nicht in dem Maße Erfolge feiern konnte wie in der Server-Welt, ist der Java-Komponenten-Markt nicht so umfassend wie der für die Windows-Tools.
Dennoch ließ sich dieses Modell auch auf die Server-Welt übertragen. Auch hier ist es wünschenswert, mittels Introspektion und Reflection Informationen zu beteiligten Objekten ermitteln zu können. Damit dies gelingen kann, ist eine Namens-Konvention unerlässlich, um bspw. Arbeits-Methoden von Methoden unterscheiden zu können, die den Zugriff auf gekapselte Attribute ermöglichen. Diese Namens-Konvention ist inzwischen so üblich, dass sie fast schon nicht mehr erwähnenswert scheint, dennoch wollen wir sie kurz vorstellen.
Grundregel jeder objektorientierten Programmierung ist es, die Attribute eines Objekts nach außen zu kapseln, den Zugriff auf diese also nur über die Methoden des Objekts zu ermöglichen. Dies gilt auch für den direkten Zugriff. Der Kontostand eines Konto-Objekts soll also bspw. nur mittels getKontostand(), withdrawAmount() und depositAmount() genutzt bzw. durch den Konstruktor beim Anlegen des Kontos gesetzt werden können. Diese Methoden nutzen dann intern das gekapselte Attribut, um ihre jeweilige Aufgabe zu erfüllen.
Mitunter ist es wichtig, direkt die Attribut-Werte zu ermitteln und ggf. auch setzen zu können. So wurde bspw. bei dem Konto-Objekt gerade mit getAmount() die direkte Abfrage ermöglicht. Hier greift nun die Namens-Konvention. Zu jedem Attribut, das zugängig sein soll, werden spezielle Setter- und Getter-Methoden geschrieben. Diese werden durch ein vorangestelltes "set" bzw. "get" und eine Wandlung des ersten Buchstabens des Attributs in einen Großuchstaben gebildet. Also für das private Attribut amount des Konto-Objekts die Methode getAmount() zum Lesen des Attributs. Damit kann man auch steuern, ob ein Attribut nur direkt lesbar sein soll (nur eine get-Methode) oder nur schreibbar sein soll (nur eine set-Methode). In unserem Konto-Beispiel war das Attribut "amount" direkt nur lesbar, da keine setAmount()-Methode existierte. Veränderungen des Kontostands waren nur indirekt über die Methoden depositAmount() und withdrawAmount möglich. De facto ist eine Gleichheit zwischen dem Namensteil hinter dem get/set und dem eigentlichen Attribut nicht wirklich notwendig, aus Sicht der Introspektion heißt das Attribut entsprechend den Methodennamen. Ob ein entsprechendes Attribut wirklich existiert, oder in den setter- und getter-Methoden ein anderes Attribut verwandt wird, ist hier irrelevant. Dies ist aber dennoch sinnvoll einzuhalten, um die Wartbarkeit des Codes zu erleichtern. Im Übrigen nehmen einem die meisten integrierten Entwicklungsumgebungen wie bspw. intellij, Eclipse oder NetBeans die Generierung der setter- und getter-Methoden anhand der vorhandene Attribute der Klasse auf Knopfdruck ab.
Warum nun so kompliziert, warum muss man extra setter-Methoden haben? Ist das nicht nur ein unnötiger Overhead? Der Vorteil gekapselter Attribute liegt zum einen darin, dass nun Sicherheitsabfragen möglich sind. Darf bspw. der Abfrager den Kontostand überhaupt einsehen? Vor allem aber ist beim Setzen eines Wertes eine Konsistenz- und Regel-Prüfung möglich. Darf das Konto nicht überzogen werden, so kann man dies innerhalb der withdrawAmount()-Methode problemlos gewährleisten. Man kann dies aber nicht dort, wo das Attribut selbst direkt frei zugänglich wäre. Denn dann müsste die Verifikation bei jeder Nutzung durch andere Objekte geschehen, und eine einfache Änderung der Verifikations-Regel wäre nicht mehr nur an einer Stelle möglich. Und zudem wird auch ermöglicht, reine Lese-Attribute zu haben (Attribute, zu denen nur getter-Methoden existieren) oder ggf. auch reine Schreib-Attribute. Insofern wird ein möglicherweise existierender minimaler Performance-Verlust deutlich durch die gewonnenen Vorteile aufgewogen.

Zum SeitenanfangZum Seitenanfang

Wozu werden Beans im JSP-Kontext genutzt

Innerhalb der Java-basierten Webentwicklung werden Beans in erster Linie zur Bereitstellung von Daten für die Präsentationsschicht, also für die JSPs, genutzt. Dazu werden zuvor die Daten für eine View aufbereitet, und innerhalb der JSP kann man nun die Daten aus den Beans mittels der im weiteren Verlauf des Kapitels vorgestellten Standard-Aktionen ausgeben. Zudem sieht die JSP-Spezifikation für eine Webseiten-Entwicklung, die nur auf JSPs beruht (sogenanntes Modell 1; siehe dazu das Kapitel "Anwendungsarchitektur") die bequeme Speicherung von Formulardaten in JSPs vor. Das weit verbreitete Struts-Framework (vgl. dazu Kapitel "[internalLink dbCaption=struts}" des Tutorials) nutzt sogenannte ActionForm-Objekte, die zusätzlich zur Datenspeicherung auch die Aufgabe der Daten-Validierung vornehmen.
JSPs machen durchweg starken Gebrauch von Beans. Die im Folgenden beschriebenen Standard-Aktionen sind dabei nur eine Möglichkeit. Des Weiteren kommt den Beans auch im Rahmen der Expression Language und der Java Standard Tag Library große Bedeutung zu. Daher wird das Thema in diesem Kapitel nicht vollständig erfasst, sondern im Laufe des Tutorials immer wieder aufgegriffen werden.
Mit der JSP-Version 2.0 ist es nun so, dass die etwas weiter hinten im Kapitel vorgestellten Tags kaum noch eine Rolle spielen und eigentlich vollständig von der Expression Language und den Standard Tags der JSTL ersetzt wurden (s. dazu ausführlich die entsprechenden Kapitel "Expression Language" und "Java Standard Tag Library (JSTL)"). Die hier vorgestellten Tags sind aber nach wie vor unverzichtbar in den zahlreichen Projekte, die noch nach dem Vorgängerstandard entwickelt werden, oder in Altprojekten, die weiterhin gewartet werden müssen.

Zum SeitenanfangZum Seitenanfang

Die Gültigkeitsbereiche

Bereits im Kapitel zu den impliziten Objekten wurde beim session-Objekt darauf hingewiesen, dass das HTTP-Protokoll zustandslos ist und Informationen über den Client zwischen dem Aufruf verschiedener Seiten verloren gehen (ausführlicher dazu im Anhang I: HTTP-Grundlagen). Im Rahmen der Entwicklung von Webapplikationen ist dies nicht immer das gewünschte Verhalten. So möchte man manchmal Daten haben, die nur während eines Requests gültig sind, andere sollen für die gesamte Dauer einer Nutzung der Website durch einen Besucher zur Verfügung stehen (bspw. Login-Informationen) und wiederum andere möchte man evtl. sogar für die gesamte Applikation vorrätig halten.
Dieser Notwendigkeit hat SUN bei der Entwicklung des JSP-Standards Rechnung getragen und so für JSPs vier Gültigkeitsbereiche (scopes) festgelegt2.
Diese Bereiche sind nach der Dauer der Verfügbarkeit der Daten und der Anzahl der Nutzer gestaffelt:

Name Bedeutung
application Mit dem Starten der Applikation (bspw. durch Hochfahren des Tomcat) steht dieser Gültigkeitsbereich bis zum Beenden der Applikation zur Verfügung. Dieser ist der umfassendste Gültigkeitsbereich und sollte nur für Attribute genutzt werden, die wirklich für die gesamte Applikation von Bedeutung sind.
session Eine Session umfasst eine Nutzersitzung und umfasst mehrere Anfragen. So kann der Status einer Benutzers während der Nutzung gespeichert werden (bspw. ein Warenkorb). Ein ausführliches Kapitel zum Umgang mit Session und dabei zu Beachtendes folgt im weiteren Verlauf des Tutorials.
request Dieser Gültigkeitsbereich umfasst genau eine Anfrage eines Nutzers. Aufgrund eines möglichen Forwardings der Anfrage an weitere Servlets bzw. JSPs kann ein Request sich über mehrere JSPs oder Servlets erstrecken.
page Dieser Gültigkeitsbereich existiert nur in JSPs und ist nur innerhalb genau einer JSP gültig. Bei einem Forwarding gehen Attribute dieses Gültigkeitsbereichs verloren.


In jeden dieser Gültigkeitsbereiche kann man Objekte in Form von benannten Objekten ablegen und diese wieder abrufen. Dazu haben alle Objekte die Methoden setAttribute(String, Object) und getAttribute(String). Der Gültigkeitsbereich "page" hat übrigens nichts mit dem impliziten Objekt "page" zu tun, sondern entspricht vielmehr dem impliziten Objekt "pageContext".
Wichtig werden diese Gültigkeitsbereiche vor allem bei den folgenden Tags für die Bean-Behandlung und bei der in einem späteren Kapitel vorgestellten Expression Language.
Eine Gegenüberstellung von Gültigkeitsbereich, dem diesen entsprechenden impliziten Objekten und den tatsächlichen Interfaces findet sich in der folgenden Tabelle:

Gültigkeitsbereich Implizites Objekt Interface
page pageContext javax.servlet.jsp.PageContext
request request javax.servlet.http.HttpServletRequest
session sessionjavax.servlet.http.HttpSession
application application javax.servlet.ServletContext

Zum SeitenanfangZum Seitenanfang

<jsp:useBean>

Dies ist der Standard-Tag (auch Standard-Aktion genannt), um Beans in JSPs nutzbar zu machen. Somit muss dieser Tag stets den später vorgestellten Tags <jsp:getProperty> und <jsp:setProperty> vorausgehen.
Dieser Tag kann auf vielfältige Weise genutzt werden. Er ermöglicht, Objekte, die bereits in einem Gültigkeitsbereich vorhanden sind, mittels der folgenden Tags zugänglich zu machen oder ggf. auch neue Objekte zu erzeugen und in einem definierten Scope abzulegen.

Das "id"-Attribut


Aufgrund seiner Möglichkeiten gibt es verschiedene mögliche Attribut-Kombinationen für diesen Tag. Lediglich das Attribut "id" muss immer gesetzt werden. Mittels dieses Attributs wird der Name der Bean festgelegt, unter dem diese Bean im angegebenen Scope gesucht und ggf. abgelegt wird und den die in den folgenen Abschnitten vorgestellten Tags verwenden, um dieses Objekt zu nutzen.

Das "scope"-Attribut


Alle mit dem Tag <jsp:useBean> angelegten Objekte gehören einem der in diesem Kapitel vorgestellten Gültigkeitsbereiche an. Dieser wird mittels des "scope"-Attributs festgelegt. Der Default-Scope ist der page-Scope und muss nicht angegeben werden. Da aber häufig Objekte genutzt werden, die vom FrontController erzeugt und bereit gestellt wurden, wird man zumeist jedoch eher den Gültigkeitsbereich auf "request" oder "session" setzen.
Prinzipiell versucht der Tag als erstes, ein Objekt im angegebenen Gültigkeitsbereich zu finden. Dann vergibt der Tag lediglich einen Namen für die später vorgestellten Tags <jsp:getProperty> und <jsp:setproperty>.
Wird aber kein Objekt in dem Gültigkeitsbereich gefunden, so wird ein neues Objekt erzeugt. Die Regeln dafür ergeben sich aus den möglichen Kombinationen der beiden im folgenden vorgestellten Attribute.

Das "type"-Attribut


Jedes Objekt gehört einem bestimmten Typ an. Dieser Typ ist entweder eine Superklasse der Klasse, die zum Erzeugen des Objekts instanziiert wurde, oder eben diese Klasse selber. Ein übliches Beispiel aus der täglichen Javawelt:

List contentList = new ArrayList();

Auf der linken Seite wird hier der Typ des Objektes festgelegt, hier also List. Auf der rechten Seite wird aber ein Objekt einer konkreten Unterklasse erzeugt, eine ArrayList3. Das "type"-Attribut nun legt eben zunächst einmal den Typ auf der linken Seite fest. Das heißt, dass ein Objekt, das sich in dem angegebenen Gültigkeitsbereich befindet, von diesem Typ oder einem davon abgeleiteten Typ sein muss. Dafür könnte der Typ auch ein Interface oder eine abstrakte Klasse sein.
Wird ein neues Objekt erzeugt und nicht das im Folgenden vorgestellte "class"-Attribut mit benutzt, so muss der Typ eine konkrete Klasse bezeichnen, und es wird ein neues Objekt dieser Klasse erzeugt. Somit ist auch klar: Das "type"-Attribut kann alleine genutzt werden (vom "id"-Attribut mal abgesehen) oder in Kombination mit dem "class"-Attribut.
Es muss stets der volle Name des Typs inklusive seines Packages angegeben werden.

Das "class"-Attribut


Das "class"-Attribut kann ebenfalls entweder alleine stehen oder in Kombination mit dem "type"-Attribut. Die angegebene Klasse muss immer eine konkrete Klasse sein, die direkt instanziiert werden kann. Steht das "class"-Attribut alleine, so wird ein Objekt dieser Klasse erzeugt, wenn kein Objekt im angegebenen Gültigkeitsbereich gefunden wird. Der Typ des Objekts und die Klasse des Objekts stimmen in diesem Fall überein. Ist zusätzlich das "type"-Attribut angegeben, so muss dieses einen Super-Typ des "class"-Attribut-Typs spezifizieren.
Die mit "class" spezifizierte Klasse muss im übrigen public sein und einen öffentlichen Konstruktor ohne Argumente, bzw den Default-Konstruktor besitzen. Logisch, denn sonst könnte schließlich kein Objekt dieser Klasse vom Tag erzeugt werden. Auch beim "class"-Attribut muss wieder der volle Klassenname inklusive des Package-Namens angegeben werden.
Interessant ist das Verhalten, wenn ein "class"-Attribut in Kombination mit dem "type"-Attribut verwendet wird, das Objekt aber bereits im angegebenen Scope vorhanden ist. Dann nämlich - und nur dann - ist das Objekt nicht unbedingt auch ein konkretes Objekt der im "class"-Attribut angegebenen Klasse. Denn einzige Bedingung ist, dass das Objekt vom Typ des "type"-Attributs ist. Auf das Beispiel des vorigen Abschnitts bezogen, würde das bedeuten, dass Objekte, die neu erzeugt würden, immer Objekte der Klasse ArrayList wären. Im Scope gefunden könnte das Objekt aber bspw. auch eine LinkedList sein.

Das "beanName"-Attribut


Dieses Attribut ist ein völliger Außenseiter und uns in der Praxis noch nicht begegnet. Der Vollständigkeit halber sei es trotzdem kurz vorgestellt.
Mit dem "beanName"-Attribut wird es möglich, entweder ein serialisiertes Objekt zu laden oder ein Objekt einer Klasse zu erzeugen. Das serialisierte Objekt muss unter dem angegebenen Namen plus der Dateinamenserweiterung ".ser" im Dateisystem zu finden sein. Bspw. wird aus "mydata.myBean" die gesuchte Datei "mydata/myBean.ser". Im Falle einer Klasse bezeichnet der Name wie bei dem "class"-Attribut den vollen Klassen-Namen. Im Unterschied zum "class"-Attribut kann das "beanName"-Attribut auch aus einem Laufzeit-Ausdruck (bspw. einer Expression oder einem EL-Ausdruck) bestehen. Z.B. <jsp:useBean id="myBean" beanName="<%= useThisOne %>" />.

Zum SeitenanfangZum Seitenanfang

<jsp:getProperty>

Mit dem Tag <jsp:getProperty> wird im Anschluss an den zuvor vorgestellten Tag <jsp:useBean> auf die Attribute der dort definierten Bean zugegriffen. Der Gebrauch des Tags ist sehr simpel, was seinen Nutzen glücklicherweise enorm steigert. Es gibt genau zwei Attribute, "name" und "property", die beide verpflichtend sind.

Das "name"-Attribut


Mit dem "name"-Attribut spezifiziert man die Bean, die man nutzen will. Dabei muss der Wert des Attributs "name" dem Wert entsprechen, der zuvor mit dem "id"-Attribut des Tags <jsp:useBean> bei der Definition der Bean festgelegt wurde.

Das "property"-Attribut


Das "property"-Attribut hingegen gibt einfach an, welche Property der entsprechenden Klasse genutzt werden soll. Dabei ist hier mit Property de facto eine getter-Methode gemeint.

Zum SeitenanfangZum Seitenanfang

<jsp:setProperty>

Dieser Tag ist wiederum deutlich komplizierter. Da er in der Praxis kaum eine Rolle spielt, wird seine Funktion hier nur kurz angerissen.
<jsp:setProperty> dient dazu, Formulardaten einer vorhergehenden Seite auszulesen und in der mit <jsp:useBean> definierten Bean abzulegen. Dabei nimmt der Tag automatisch die geeignete Konvertierung in das passende Format der Property der Bean vor, also bspw. werden Zahl-Werte in int- oder double-Werte umgewandelt. Welche Konvertierung anzuwenden ist, erkennt der Tag mittels Introspection anhand der Definition der geeigneten setter-Methode der Bean.
Vier Attribute können nun gesetzt werden, um das Verhalten zu steuern.

Das "name"-Attribut


Es funktioniert genauso wie bei dem <jsp:getProperty>-Tag. Mit dem Attribut wird einfach bestimmt, auf welche der zuvor mit <jsp:useBean> definierten Beans zugegriffen werden soll. Der Inhalt des Name-Attributs muss dabei dem Inhalt des "id"-Attributs des useBean-Tags entsprechen.

Das "property"-Attribut


Wie schon bei <jsp:getProperty> wird hier angegeben, welches Attribut verwandt werden soll, genauer, welche setter-Methode angesprochen werden soll. Es gibt aber eine Besonderheit bei dem <jsp:setProperty>-Tag: Bei diesem kann als Wert auch der Asterisk "*" verwandt werden, um dem Tag mitzuteilen, dass er alle Properties der Bean setzen soll, zu denen er im Request Daten vorfindet. Das wiederum funktioniert nicht immer so, wie man sich das vorstellt. Es werden nämlich zuvor keinerlei Daten gelöscht. Hatte bspw. die Property "author" vor dem Aufruf der JSP (bspw. bei einer Bean, die in der Session liegt) den Wert "Jane Doe", so wird dieser beibehalten, wenn sich im Request kein Parameter für diese Property befindet. Dieses Verhalten kann schnell zu unschönen Überraschungen führen. Hier ist daher große Vorsicht angebracht.

Das "param"-Attribut


Mit "param" wird festgelegt, welcher Request-Parameter zum Setzen der Property genutzt werden soll. Wenn also bspw. ein der JSP vorgeschaltetes Formular "Autor" statt "author" verwendet, kann man an dieser Stelle die Zuordnung vornehmen. Wird das "param"-Atribut nicht gesetzt, so wird automatisch nach einem Request-Parameter gesucht, der dem Namen des "property"-Attributs entspricht.

Das "value"-Attribut


Dieses Attribut wiederum kann genutzt werden, um einen expliziten Wert für die Property zu setzen. Da hierfür nicht auf Request-Parameter zugegriffen wird, kann dieses Attribut nicht zusammen mit dem "param"-Attribut verwandt werden.
Soweit die Beschreibung dieses Tags. In der Praxis wird er allerdings nur selten eingesetzt. Der Grund liegt darin, dass er eigentlich nur in Projekten Sinn macht, in denen kein FrontController eingesetzt wird. Und diesen Projekte wiederum fehlt es häufig genug an einer Struktur, um diesen Tag gewinnbringend einsetzen zu können.

Zum SeitenanfangZum Seitenanfang

Anmerkungen:

1) Die Java-Mechanismen, die es ermöglichen, programmatisch Informationen bspw. zu Attributen oder zu Methoden, von Java-Typen zu erfragen. (zurück)

2) Das gilt so aber uneingeschränkt auch für alle anderen mir bekannten Webtechnologien. Es existieren durchweg die gleichen Gültigkeitsbereiche. (zurück)

3) Man unterscheidet zwischen dem statischen Typ eines Attributs, der zur Übersetzung festgelegt wird und dem dynamischen Typ, der erst zur Laufzeit festgelegt wird. Der dynamische Typ kann prinzipiell jedwede Unterklasse des statischen Typs sein. (zurück)


www.jsptutorial.org
© 2005, 2006, 2007