<jsptutorial />

Lebenszyklus von JSPs


Grundlegendes

Für die Entwicklung mit JavaServer Pages ist es wichtig zu verstehen, welche Phasen eine JSP-Datei bei ihrem Weg durch den JSP-Container durchläuft. Bereits in der Einleitung haben wir des öfteren von JSP/Servlet-Containern geredet. Dies erklärt sich daraus, dass jeder JSP-Container stets auch ein Servlet-Container1 sein muss. Und vor allem aus der Tatsache, dass jede JSP im Verlaufe Ihres Lebenszyklus vom Container zunächst in ein Servlet transformiert werden muss. Zur Ausführung kommen letztlich immer nur Servlets. Für die folgende genaue Beschreibung des Lebenszyklus muss man ein paar grundlegende Dinge über Servlets wissen. Die wesentlichen Informationen zu Servlets werden im Anhang II: Servlets - Die Grundlagen gegeben.
Aber was ist denn nun ein Servlet? Im Wesentlichen eine Java-Klasse. D.h. eine Java-Datei, die wie üblich mit dem Java-Compiler in Bytecode übersetzt wird. Das besondere Merkmal von Servlets betseht lediglich darin, dass spezielle Interfaces aus dem Package javax.servlet, bzw. javax.servlet.http implementiert werden müssen. Zudem gibt es ein paar Exceptions und Convenience-Klassen in den beiden Packages. Im Folgenden werden wir die drei für das Verständnis von JSPs wesentlichen Interfaces kurz beschreiben. Zusätzlich sollte unbedingt die genauere Dokumentation der Klassen und Interfaces (sowie der Packages) in der Javadoc-Beschreibung der J2EE-API angeschaut werden. Einige dieser Klassen bzw. Interfaces begegnen einem in der Praxis mit JSPs immer wieder.
Das Package javax.servlet ist grundlegenderer Natur und im Wesentlichen Protokoll-agnostisch, wohingegen das Package javax.servlet.http ganz konkrete Sub-Interfaces und Subklassen für den HTTP-Transfer von Client-Anfragen und Server-Antworten enthält. Soweit im javax.servlet.http-Package Interfaces das entsprechende Basis-Interface aus javax.servlet erweitern, werden beide zusammen erklärt. Ein Verständnis des HTTP-Protokolls ist für die folgenden Abschnitte unbedingt notwendig. Ggf. finden Sie alle wesentlichen Informationen dazu im Anhang I: HTTP-Grundlagen.


Sehen wir uns dazu kurz ein einfaches Beispiel an:
package org.jsptutorial.examples.servlets;

import javax.servlet.http.*;
import javax.servlet.*;

import java.io.*;

/**
 * A simple Servlet for demonstration-purposes only.
 */
public class SimpleServlet extends HttpServlet {

   public void doGet(HttpServletRequest request, HttpServletResponse response)
           throws IOException, ServletException{

      // 1. process request-parameters and -headers...
      String userName = request.getParameter("userName");
      if (userName == null) {
         userName = "Stranger";
      }
      String client = request.getHeader("User-Agent");
      // 2. prepare the response...
      response.setContentType("text/html");
      // 3. get access to a PrintWriter or to an OutputStream...
      PrintWriter out = response.getWriter();
      // 4. write to PrintWriter or OutputStream...
      out.println("<html>");
      out.println("<head>");
      out.println("<title>JSP Tutorial - SimpleServlet</title>");
      out.println("</head>");
      out.println("<body>");
      out.println("Hello " + userName + "<br>You are using: " + client);
      out.println("</body></html>");
   }
   
   
   protected void doPost(HttpServletRequest request, HttpServletResponse response)
         throws IOException, ServletException {
      // let doGet() do all the work...
      doGet(request, response);
   }
   
}

Code im Extrafenster ausführen

Wie man sehen kann, ist die Verwendung mehrerer out.println()-Statements zur Generierung der HTML-Antwortseite extrem aufwendig und nicht sehr übersichtlich. Zudem ist dies für Tools, die üblicherweise zum HTML-Skripting verwandt werden (seien es nun WYSIWYG-Tools wie Dreamweaver, oder Code-Editoren wie HomeSite, Quanta, Bluefish oder jEdit) unbrauchbar. Die Trennung zwischen Webdesign und Programmierung der dynamischen Inhaltsanforderungen ist nicht sauber möglich.
An dieser Stelle setzen nun die JSPs ein, die genau diese Trennung versprechen einzulösen. Dazu werden reine HTML-Seiten genommen und um spezifische Tags und JSP-Angaben erweitert.3 Diese JSP-Seiten werden nun genommen und im Laufe ihres nachfolgend beschriebenen Lebenszyklus in Servlets umgewandelt, die dann bei HTTP-Anfragen zur Ausführung kommen.

Zum SeitenanfangZum Seitenanfang

Übersetzungsphase

In dieser Phase wird aus der JSP-Seite der Servlet-Quellcode erstellt. Dies übernimmt der Container, wenn entweder die Seite das allererste mal aufgerufen wird, oder wenn der Zeitstempel des Quellcodes neuer ist als der Zeitstempel des bei einem früheren Aufruf evtl. bereits generierten Servlet-Quellcodes4. Eine Änderung an einer JSP führt also automatisch zu einer Neu-Übersetzung. Aber hier ist auch mitunter Vorsicht angebracht. Eine Datei erst umzubenennen, dann vorübergehend eine andere Datei gleichen Namens zu verwenden (bspw. eine spezielle Debug-Version oder eine andere Index-Seite zur Bekanntgabe von Wartungsarbeiten, etc.) und anschließend die alte Datei zurückzukopieren, funktioniert nicht. Jedenfalls nicht, sofern der Zeitstempel nicht ebenfalls verändert wird (unter UNIX-Systemen ist bspw. ein touch xyz.jsp notwendig).
In dieser Phase erkennt der Übersetzer nur Fehler, die direkt mit der JSP-Syntax zusammenhängen. Bspw. wenn ein öffnender Skriptlet-Tag nicht geschlossen wird, falsche Direktiven verwendet werden, Taglibraries nicht gefunden werden etc. Welche Fehler hier alle auftreten können, wird im Laufe des Tutorials noch gezeigt. Fehler, die im Java-Code selber enthalten sind, werden in dieser Phase noch nicht gefunden. Daher ist die Anzeige der Fehler in dieser Phase und der Fehler der folgenden Phase auch häufig unterschiedlich detailliert.
Das generierte Servlet muss das Interface JspPage bzw. bei dem zumeist wohl verwendeten HTTP-Protokoll das davon abgeleitete Interface HttpJspPage implementieren. Das genaue Vorgehen dabei ist Container-spezifisch. Im Falle von Tomcat ist die Basis jedes aus einer JSP generierten Servlets die abstrakte Klasse org.apache.jasper.runtime.HttpJspBase.

Zum SeitenanfangZum Seitenanfang

Compilephase

Hier wird vom Container der gewöhnliche Java-Compiler angeworfen, um aus dem generierten Servlet-Quellcode eine Java-class-Datei zu erzeugen.
In dieser Phase werden nun die Fehler im ggf. vorhandenen Java-Code der JSP entdeckt, wie Syntax-Fehler, nicht vorhandene Klassen, die per import-Statements referenziert werden, etc.
Da die Übersetzung und Kompilierung für den ersten Aufruf der Seite vergleichsweise viel Zeit in Anspruch nehmen, gibt es auch die Möglichkeit, diese beiden Phasen mittels spezieller Precompilation-Tools zusammenzufassen. Es ist auch ein Aufruf der Art http://www.jsptutorial.de/index.jsp?precompile=true möglich, der diese beiden Phasen anstößt5. Bei Tomcat existiert für die Übersetzung mittels des Jasper-Compilers6 eine Klasse JspC, die insbesondere im Zusammenhang mit Ant-Deployments Sinn macht.

Zum SeitenanfangZum Seitenanfang

Class-Loading und Instanziierung

Jede virtuelle Maschine besitzt einen Classloader zum Laden der benötigten Klassen. Da auch der Servlet-Container in einer JVM abläuft (oder den Servlets eine solche zur Verfügung stellt), muss auch für Servlets zunächst der entsprechende Bytecode in den Arbeitsspeicher geladen werden und dann ein passendes Objekt instanziiert werden, bevor es aufgerufen werden kann. Dies geschieht in dieser Phase und unterscheidet sich in keiner Weise von dem normalen Verhalten der JVM.

Zum SeitenanfangZum Seitenanfang

jspInit()

Die Methode jspInit wird aufgerufen, um die zu der JSP gehörige Servlet-Instanz zu initialisieren. Sie wird aufgerufen, bevor irgend eine andere Methode, also insbesondere die nachfolgend dargestellte Methode _jspService() aufgerufen wird. Soweit notwendig, kann diese Methode verwandt werden, um Ressourcen zu akquirieren oder um die Attribute des Servlets vorzubereiten. I.a.R. ist diese Methode leer. Um das Default-Verhalten der HttpJspPage zu überschreiben, ist in der JSP ein entsprechender Deklarationstag einzufügen. Ein Beispiel dazu findet sich im Kapitel zur JSP-Syntax.

Zum SeitenanfangZum Seitenanfang

_jspService()

Dies ist die eigentliche Arbeitsmethode des generierten Servlets. Der gesamte Template-Code einer JSP-Seite, der Java-Code der Skriptlets in der JSP, JSP-Aktionen und Expression-Language Ausdrücke findet sich hier als Java-Code und Aufrufe entsprechender Bibliotheken wieder. Diese Methode wird bei jedem Aufruf einer JSP-Seite aufgerufen und generiert den gewünschten HTML-Code (bzw. XML-Code, oder welche Ausgabe letztlich auch immer mittels eines Requests vom Client angefordert wird). Wie die service-Methode originärer Servlets erhält auch diese Methode Request- und Response-Objekte, um weitere Angaben über die Anfrage des Clients erhalten und eine geeignete Antwort vorbereiten zu können.

Zum SeitenanfangZum Seitenanfang

jspDestroy()

Diese Methode bildet das logische Gegenstück zur Methode jspInit(). Etwaige Ressourcen, die in jspInit() akquiriert wurden, müssen hier ggf. wieder frei gegeben werden. Auch diese Methode wird genau einmal aufgerufen, unmittelbar, bevor der Container diese Servlet-Instanz zur GarbageCollection frei gibt.

Zum SeitenanfangZum Seitenanfang

Anmerkungen:

1) Im Folgenden werden JSP-Container und Servlet-Container stets austauschbar verwandt. Mitunter wird auch der Ausdruck JSP-Engine, oder Servlet-Engine verwendet. (zurück)

2) Das HTTP-Protokoll definiert ganzzahlige Status-Codes, um anzugeben, ob die Verarbeitung des Requests fehlerfrei verlief, bzw. welche Fehler auftraten. Das Interface javax.servlet.http.HttpResponse enthält für alle Statuscodes passende Konstanten (zurück)

3) Inwieweit dieser Anspruch eingelöst werden kann, werden wir in späteren Kapiteln noch ausführlich erörtern. An dieser Stelle nur soviel: Erst der Verbund von Servlets und JSPs - idealerweise in Verbindung mit verschiedenen Software-Design-Pattern - macht die gewünschte Trennung vollständig. Doch dazu, wie gesagt, später mehr. (zurück)

4) Ein Zeitstempel ist der exakte Zeitpunkt der Erzeugung bzw. der letzten Änderung der Datei und ist als Attribut dieser Datei fest zugeordnet. (zurück)

5) Allerdings muss ein JSP-Container diesen Aufruf nicht unterstützen. Ein extra Aufruf dieser Art dürfte für komplexere Anwendungen ohnehin wenig sinnvoll sein. Wenn man die Verzögerung vermeiden möchte, ist man mit den speziellen Container-Tools immer besser dran. (zurück)

6) Der Tomcat-JSP-Compiler heißt Jasper (Pakete org.apache.jasper etc.), der Servlet-Container Catalina (Pakete org.apache.catalina etc.) und der mitgelieferte Web-Connector Coyote (Pakete org.apache.coyote etc). (zurück)


www.jsptutorial.org
© 2005, 2006, 2007