Results for category "Fachinformatiker ausbilden"

4 Articles

Apache Tomcat für Azubis

Nachdem ich vor ein paar Wochen den Artikel über Spring für Azubis veröffentlicht habe, wird es heute Zeit für “Apache Tomcat für Azubis”.
Dieser Artikel hat nicht ansatzweise den Anspruch, die komplette Konfiguration eines Tomcat-Servers zu beschreiben – dafür ist die offizielle Dokumentation gedacht. Hier geht es darum die grobe Funktionsweise zu erklären. Im Folgenden definiere ich Web-Applikation als eine Anwendung, die in Java geschrieben ist und in einem Applikationsserver läuft.

Was ist der Apache Tomcat?

Spätestens ein paar Stunden nach dem Einstieg in die Web-Programmierung mit Java stellt sich die Frage: “Wie bekomme ich den Mist überhaupt zum Laufen?”. Von der Entwicklung mit PHP ist man gewöhnt, dass die Applikation nur in eines der Verzeichnisse des Web-Servers geschoben werden muss und das war es dann schon – klar, PHP muss vorher noch in den Webserver als CGI oder Modul eingebunden sein.

Der Apache Webserver liefert bei einer Anfrage über HTTP oder HTTPS die jeweiligen (geparsten) Dateien zurück.
Nichts Anderes macht der Apache Tomcat auch. Tomcat ist ein Webserver, der speziell auf die Anforderungen von Web-Applikationen unter Java zugeschnitten ist.

Spezifikationen

Einige Hintergrundinfos sind nötig: Über den Java Community Process (JCP) werden in regelmäßigen Abständen so genannte Java Specification Requests (JSR) veröffentlicht. Diese beinhalten Standardisierungen für die Java-Umgebung. Oracle, damals Sun, hat 1997 mit der Version 1.0 die erste Version der Servlet-API definiert und diese mit der Version 2.3 im August 2001 dem JCP zugängig gemacht. Seit dieser Zeit werden Änderungen in der Servlet-API gemeinsam von der Java-Community bestimmt.

Die Servlet-API beschreibt anhand von Interface-Definitionen, welche Schnittstellen ein Applikations-Server implementieren bzw. bereitstellen muss.

Warum die Spezifikation?

Für verschiedene Anwendungsfälle gibt es verschiedene (Java-)Applikationsserver. Der bekannteste unter ihnen ist sicherlich Apache Tomcat. Daneben gibt es noch Jetty (freie Implementierung, ist relativ klein und kann in Embedded-Umgebungen gut eingesetzt werden), Websphere (kommerzielle Lösung von IBM) und einige andere.

Damit nun eine Applikation unter allen Applikationsserver funktionieren kann, muss jeder Server die Servlet-API bereitstellen. Oracle/Sun sichert über Test-Suiten ab, dass die Applikationsserver auch mit der Servlet-API kompatibel sind und den Anforderungen in dem JSR entsprechen.

Der Entwickler einer Webapplikation kann sich nun sicher sein, dass die Anwendung unter jedem Applikationsserver lauffähig ist. Die Entscheidung, welcher Server einzusetzen ist, hängt von den Anforderungen des Kunden oder des persönlichen Geschmacks ab.

Versions-Wirr-War(r)

Mit jeder neuen Version der Servlet-API werden neue Funktionalitäten aufgenommen, die die aktuellen Anforderungen im Web-Bereich widerspiegeln.

Wenn man sich den Eintrag in der englischen Wikipedia zu Servlets anschaut sieht man, dass einige Versionen existieren. Aktuell ist die Version 3.0, die im JSR 315 definiert ist. Eine der größten Erweiterungen ist die Definition von asynchronen Servlets, die durch die starke Verbreitung von AJAX-Funktionalitäten im Internet längst überfällig gewesen ist. Der Apache Tomcat 7 implementiert die Version 3.0 der Servlet-API und kann somit auch die neuen Funktionaliten bereitstellen.

Allerdings wird bei vielen Projekten weiterhin die letzte Version 2.5 eingesetzt – hierfür wird der Apache Tomcat 6 eingesetzt. Da Version 3.0 aber i.a.R. abwärtskompatibel ist, können für die Servlet-API 2.5 auch der Tomcat 7 eingesetzt werden.

Übersicht über die Servlet-API

Die Spezifikation für JSR 153/Servlet 2.5 ist knapp 350 Seiten lang, diese hier alle zusammenzufassen wäre unsinnig. Statt dessen will ich kurz die wichtigsten Themen behandeln.

Jede Web-Applikation basiert auf einem oder mehreren Servlets. Servlets sind Klassen, die von der abstrakten Klassen HttpServlet erben.Diese stellt Methoden bereit, die beim Aufruf des Servlets aufgerufen werden. doGet(…) wird bei einem HTTP-GET-Request, doPost(…) bei einem HTTP-POST-Request u.s.w.

Der Entwickler implementiert nun diese Methoden und kann über die Parameter vom Typ HttpServletRequest (eingehender HTTP-Request) und HttpServletResponse (zu erzeugende HTTP-Antwort) die Ein- und Ausgabe des HTTP-Streams steuern.

Klingt einfach, ist es an sich auch. Frameworks wie Spring kapseln all ihre Methoden um diese drei wichtigsten Klassen – und natürlich noch einigen anderen.

JavaServer Pages / JSP

JSP? WTF? Von PHP sind sicherlich Dateien bekannt, die wild PHP-Quellcode und HTML-Ausgabe mischen. JSP-Dateien sind das Gegenstück zu eben diesen PHP-Dateien. In ihnen kann HTML- und Java-Code parallel stehen.

Intern passiert beim Aufruf einer JSP-Datei nichts weiter, als dass bei ihrem ersten Aufruf die Datei in ein Servlet umgewandelt wird. Alle HTML-Fragemente werden über out.println() ausgegeben. Bei erneutem Aufruf der Datei wird nun das cachete Servlet ausgeführt. Sollte sich die JSP-Datei ändern, wird sie beim nächsten Aufruf neu kompiliert.

Datei web.xml und Ordner WEB-INF

Jede Web-Applikation muss im Hauptverzeichnis den Ordner WEB-INF und darin die Datei WEB-INF/web.xml besitzen. Der Inhalt des Ordners WEB-INF kann von außerhalb, d.h. vom Benutzer nicht aufgerufen werden. Er enthält für die Applikation alle wichtigen Dateien, dazu zählen die kompilierten Java-Dateien (Ordner WEB-INF/classes) und eben die Datei web.xml. Diese beschreibt die grundlegende Konfiguration der Applikation. Die web.xml ist ein so genannter Deployment Descriptor.

web.xml

Kommen wir nun zum simplen Aufbau des Deployment Descriptors

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        version="2.5">
       <env-entry>
                <env-entry-name>environmentVariable</env-entry-name>
                <env-entry-value>true</env-entry-value>
                <env-entry-type>java.lang.Boolean</env-entry-type>
        </env-entry>
        <servlet>
                <servlet-name>myServlet</servlet-name>
                <servlet-class>de.ckl.servlet.MyServlet</servlet-class>
                <description>Serlvet</description>
                <init-param>
                        <param-name>debug</param-name>
                        <param-value>true</param-value>
                </init-param>
                <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet>
                <servlet-name>myLaterServlet</servlet-name>
                <servlet-class>de.ckl.servlet.MyLaterServlet</servlet-class>
                <description>Spaeter geladenes Servlet</description>
                <init-param>
                        <param-name>debug</param-name>
                        <param-value>false</param-value>
                </init-param>
                <load-on-startup>10</load-on-startup>
        </servlet>
        <servlet-mapping>
                <servlet-name>myServlet</servlet-name>
                <url-pattern>/servlet/*</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
                <servlet-name>myLaterServlet</servlet-name>
                <url-pattern>/later/*</url-pattern>
        </servlet-mapping>
</web-app>

Das Root-Tag web-app enthält den zu benutzenden Namespace und die dahinter liegende XSD. Wenn die Zuordnung nicht stimmt, kann vom Applikationsserver die Anwendung nicht gestartet werden. Also Achtung!

Das Tag env-entry legt anhand von Key-Value-Paaren applikationsweite Einstellungen fest, die für alle Servlets der Applikation gültig sind.

Über servlet werden nun die einzelnen Servlets definiert. servlet-name muss für jedes Servlet eindeutig sein, servlet-class verweist auf die Klasse, die HttpServlet implementiert. Mit init-param können Servlet-spezifische Einstellungen festgelegt werden. Im obigen Beispiel wird das Debugging für myServlet aktiviert, bei myLaterServlet hingegen deaktiviert.

Damit eine Reihenfolge zum Starten festgelegt werden kann – z.B. um Ressourcen wie Datenbankverbindungen zu initalisieren, wurde das Tag load-on-startup eingeführt. Je kleiner der Wert, desto früher wird das Servlet gestartet.

Wir haben nun also die Servlets und deren Einstellungen konfiguriert. Es geht nun darum, dass die Servlets auch im Browser aufgerufen werden. Im obigen Beispiel werden alle HTTP-Anfragen (das Verb – also GET, POST etc. ist dabei egal) auf die URL http://…/mein_context/servlet/* auf das Servlet myServlet gemappt (siehe erstes servlet-mapping-Tag), die URLs unter http://…/mein_context/later/* hingegen auf auf myLaterServlet.

Deployment

Das Einspielen der Webapplikation in den Tomcat nennt man Deployen. Entweder man hat die Applikation bereits manuell eingerichtet und kopiert die neuen Dateien einfach in das Applikationsverzeichnis oder aber man spielt ein .war ein. Ein WAR (Web Archive) ist nichts weiter als eine ZIP-Datei, die im Verzeichnis webapps des Tomcats kopiert wird. Der Tomcat überwacht den Inhalt des Verzeichnisses und extrahiert bei einer neuen .war-Datei diese in das Verzeichnis webapps/war-datei. Der Vorgang wird Hot-Deployment genannt.

Wir haben beispielsweise die Datei my-test-applikation.war. Diese wird nach webapps kopiert. Der Tomcat entpackt den Inhalt des Archivs automatisch nach webapps/my-test-applikation.

Das Archiv enthält alle Dateien, die von der Web-Applikation benötigt werden. Dazu gehören kompilierte Klassen (/WEB-INF/classes), abhängige Libraries (/WEB-INF/lib), der Deployment-Deskriptor (/WEB-INF/web.xml) und JSP-Dateien, Templates u.s.w.
Als Entwickler muss man immer im Hinterkopf behalten, dass die Anpassung der globalen conf/server.xml bzw. conf/web.xml des Tomcats während des Deployments nichts angetastet werden soll. Dadurch wird sichergestellt, dass die Applikationen zum einen einfach zu deployen und zum anderen unabhängig von dem eingesetzten Application-Server sind. In einem Jetty existiert z.B. keine server.xml!

Context Path

Die Applikation an sich ist dann unter http://localhost/my-test-applikation erreichbar. Den Pfad hinter http://localhost/ wird Context Path genannt und ist unabhängig von dem realen Ordner!

In der Datei conf/server.xml wird automatisch beim Hot Deployment ein neuer Konfigurationseintrag erzeugt:

<Host name="localhost" debug="0" appBase="webapps"
	      unpackWARs="true" autoDeploy="true">
    <Context path="my-test-applikation" docBase="webapps/my-test-applikation" debug="0"/>
</Host>

Über das path-Attribut wird festgelegt, dass alle Anfragen auf die URL http://localhost/my-test-applikation an die Applikation geht, die im Odner (docbase) webapps/my-test-applikation liegt.

Lifecycle

Beim Starten des Tomcats werden alle Applikationen, d.h. jeder definierte Context gestartet. Dazu überprüft Tomcat die Datei web.xml und startet jedes Servlet in der angegebenen Reihenfolge.

Sobald nun eine HTTP-Anfrage an den Tomcat gerichtet ist, wird zuerst überprüft, ob der Context Path definiert ist. Bei Existenz wird an das Servlet übergeben, auf dem das Mapping der URL zutrifft.

Beim Beenden des Tomcats werden alle laufenden Servlets beendet.

Wichtigster Unterschied zu PHP-Webapplikationen

Beim Umstieg von PHP-Webapplikationen zu Java gibt es neben den sprachlichen Unterschieden folgende, gravierende Differenz, die man als Entwickler immer im Hinterkopf haben muss: Unter PHP wird bei jedem Seitenaufruf das Script geparst – bei Java hingegen wird eine Applikation zu Beginn ihres Lebenszyklus gestartet und läuft die ganze Zeit!

Dafür ist es wichtig, dass man die verschiedenen Scopes kennt:

  • Web Context / Application – javax.servlet.ServletContext
    Dieser Kontext umfasst die komplette Applikation und existiert vom Start des Servlets bis zum Herunterfahren
  • Session – javax.servlet.http.HttpSession
    Eine Session unter PHP ist bekannt? Dieser Scope ist identisch dazu.
  • Request – javax.servlet.HttpServletRequest
    Der Request-Scope ist so lange verfügbar, wie eine HTTP-Anfrage dauert.
  • Page – javax.servlet.jsp.PageContext
    Bei der Arbeit mit JSP-Dateien existiert dieser Scope. In ihm sind nur Objekte verfügbar, die innerhalb der Seite definiert worden sind.

Objekte im Scope Application können von allen Servlets gesehen werden, es existiert immer nur ein Application-Scope innerhalb einer Anwendung. Session-Scopes existieren für jeden Benutzer, solange er mit der Applikation kommuniziert. Ein Benutzer kann nicht auf die Session eines anderen zugreifen (zumindest nicht ohne Tricks).
Bei 10 HTTP-Anfragen existieren auch 10 Request-Scopes, die i.a.R. jeweils einer Session zugeordnet sind.

Entwicklung mit Eclipse

Eclipse bzw. WTP bindet den Applikationsserver direkt in die IDE mit ein.

Integration

Zuerst muss unter Preferences -> Server -> Runtime Environments ein neuer Server hinterlegt werden. Dazu muss über den Button Add zuerst die verwendete Tomcat-Version ausgewählt und danach das Installationsverzeichnis angegeben werden. Unter File -> New -> Other -> Server -> Server muss danach ein neuer lokaler Server eingerichtet werden. Im folgenden Dialog muss unter Apache die verwendete Version ausgewählt werden. Die letzte Seite des Wizards mit dem Namen Add and Remove wird dazu verwendet, um Projekte vom Typ Dynamic Web Project automatisch der Tomcat-Instanz zuzuweisen.

Nachträgliche Änderung eines Projekts in Dynamic Web Project

Falls ein Projekt nachträglich in ein Dynamic Web Project umgewandelt werden soll, geschieht das über einen Rechtsklick auf den Projektordner im Project Explorer. Dort muss unter Project Facets der Eintrag Dynamic Web Module ausgewählt werden.
Die Aktivierung sorgt dafür, dass das Projekt von Eclipse als Web-Projekt erkannt wird.

Ordnerstruktur

Eclipse benutzt standardmäßig den Ordner WebContent des Projekts als Hauptverzeichnis für die Dateien, die automatisch im Tomcat deployt werden. Wenn das Verzeichnis nicht WebContent lauten soll, muss im Projektordner die Datei .settings/org.eclipse.wst.common.component angepasst werden. Dort lässt sich definieren, welche Dateien in den Tomcat deployt werden sollen.

Automatisches Deployment

Was macht nun Eclipse beim Starten der lokalen Tomcat-Instanz?

  • Zuerst werden alle Klassen kompiliert, die in einem Source-Ordner liegen.
  • Danach wird, falls noch nicht vorhanden, unter workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmpN/wtpwebapps der Ordner des Context-Paths erstellt – das N bei tmpN steht dabei für die Nummer der jeweiligen Serverinstanz
  • Anhand der XML-Datei .settings/org.eclipse.wst.common.component werden nun alle dort definierten Dateien in den Context-Path kopiert
  • Eclipse startet nun eine neue Tomcat-Instanz, bei der das Verzeichnis mit den Konfigurationsdateien auf Servers -> Apache Tomcat x.x at localhost-config gemappt wird. Somit wird sichergestellt, dass die lokale Tomcat-Installation autark von der Entwicklerinstanz betrieben werden kann.
  • Tomcat lädt die Apache Tomcat x.x at localhost-config/server.xml und erstellt einen neuen Context-Path in der Laufzeitumgebung des Tomcat
  • Alle HTTP(S)-Anfragen des Context-Paths werden somit auf den realen Pfad workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmpN/wtpwebapps/my-test-applikation gemappt

Spring Framework für Azubis: Tutorial

Einen meiner Azubis habe ich vor einigen Wochen die Aufgabe gegeben, sein in Java geschriebenes Website-Crawler Tool auf Spring zu portieren. Dabei kam es dann logischerweise zu einigen Fragen und Pitfalls, über die man als Spring-Neuling stolpert.

In diesem Artikel will ich auf die Hürden bei der Entwicklung mit Spring hinweisen, die man als Java- bzw. Spring-Neuling zwangsläufig hat. Anspruch auf Vollständigkeit ist angesichts der Komplexität von Spring natürlich nicht mal annähernd zu erwarten 🙂

Warum Spring?

Das Spring Framework besteht aus einer Vielzahl an Klassen, die wiederkehrende Aufgaben bereits implementiert haben und getestet sind. So entfällt z.B. das umständliche Erzeugen der Datenbank-Verbindungen und ähnlicher Sachen.
Spring setzt auf das Prinzip der Dependency Injection. Dahinter versteckt sich grob gesagt die Tatsache, dass Objekte innerhalb des Quellcodes nicht mehr mit dem Schlüsselwort new erzeugt werden, sondern dass die Instanzierung von Objekten durch Spring geschieht. Durch diese Tatsache wird die Abhängigkeit zwischen Klassen reduziert, einzelne Funktionalitäten lassen sich leichter austauschen. Damit einhergehend ist es auch wichtig, dass gegen Interfaces programmiert wird und nicht gegen konkrete Klassen.

Die Beispielapplikation

Ziel soll es sein, dass wir eine einfache Main-Klasse haben, die in Abhängigkeit von der Konfiguration entweder die Klasse StringHandlerBackward oder StringHandlerSecond besitzt. Beide StringHandler-Klassen implementieren das Interface IStringHandler. StringHandlerBackward gibt einen String von hinten nach vorne aus, StringHandlerSecond gibt nur jeden zweiten Buchstaben aus.

Die XML-Konfigurationsdatei I, XML & XSD

Spring setzt auf die Konfiguration durch XML-Dateien. Innerhalb der XML-Datei wird deklarotisch hinterlegt, welche Objekte mit welchen Eigenschaften automatisch bei ihrer Ansprache erzeugt werden. Im folgenden Beispiel wird unsere Spring-Konfiguration in der Datei applicationContext.xml hinterlegt. Die XML-Datei hat folgenden Aufbau:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">
</beans>

xmlns=”http://www.springframework.org/schema/beans” besagt, dass Standard-Namespace der XML-Datei http://www.springframework.org/schema/beans ist. Innerhalb des Attributs xsi:schemaLocation wird zugewiesen, dass der Namespace http://www.springframework.org/schema/beans innerhalb der XSD-Datei
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd beschrieben ist.
Eine XSD-Datei enthält Informationen darüber, wie eine XML-Datei aufgebaut sein darf (Reihenfolge der Tags/Elemente, welche Elemente benutzt werden dürfen, welche Attribute ein Tag/Element enthalten darf/muss u.s.w.).

Über den Standard-Namespace können wir nun alle Elemente aus der spring-beans-3.0.xsd direkt innerhalb unserer XML-Datei benutzen.

xmlns:context=”http://www.springframework.org/schema/context” besagt, dass wir über das Prefix context Zugriff auf den Namespace
http://www.springframework.org/schema/context haben.

Warum das so ist? Man stelle sich vor, dass beide XSD-Dateien (spring-beans-3.0.xsd und spring-context-3.0.xsd) das Element bean erlauben. Woher soll nun der XML-Parser wissen, welche Definition der bean Elements genutzt werden soll? Soll er die Definition aus
spring-beans-3.0.xsd nutzen oder aus spring-context-3.0.xsd? Mit den Prefix context wird das Problem nun umgangen, da wir über bean auf den Standard-Namespace (xmlns) zugreifen, über context:bean hingegen auf den context-Namespace (xmlns:context).

Unsere Java-Klassen

Zu der Konfigurationsdatei kommen wir gleich wieder, erst einmal müssen wir unsere trivialen Java-Klassen schreiben:

IStringHandler.java

interface IStringHandler {
  /**
  * Wandelt den String der Klasse um und gibt diesen zurück
  * @return String
  */
  public String stringify();
}

StringBase.java

class StringBase {
  private String string;

  public StringBase(String _string) {
    setString(_string);
  }

  public StringBase() {
  }

  public void setString(String _string) {
    string = _string;
  }

  public String getString() {
    return string;
  }

StringHandlerBackward.java

class StringHandlerBackward extends StringBase implements IStringHandler {
  public StringHandlerBackward(String _string) {
    super(_string);
  }

  public StringHandlerBackward() {
  }

  public String stringify() {
    StringBuffer sb = new StringBuffer();
    String ref = getString();

    for (i = (ref.length - 1); i >= 0; i--) {
      sb.append(ref.charAt(i));
    }

    return sb.toString();
  }
}

StringHandlerSecond.java

class StringHandlerSecond extends StringBase implements IStringHandler {
  public StringHandlerSecond(String _string) {
    super(_string);
  }

  public StringHandlerSecond() {
  }

  public String stringify() {
    StringBuffer sb = new StringBuffer();
    String ref = getString();

    for (i = 0, m = ref.length; i <= m; i=i+2) {
      sb.append(ref.charAt(i));
    }

    return sb.toString();
  }
}

Main.java

class Main {
  public static void main(String args[]) {
  }
}

Der ApplicationContext und Resourcen

Spring definiert den so genannten ApplicationContext, der Methoden bereit stellt, um auf die in der XML-Datei hinterlegten Beans zuzugreifen. Für unsere Zwecke soll die Klasse ClasspathXmlApplicationContext dienen. Diese implementiert das Interface ApplicationContext und noch andere Interfaces, die die Arbeit erleichtern. ClasspathXmlApplicationContext ist für die Arbeit mit XML-Konfigurationsdateien innerhalb des Java-Classpath gedacht.

Mit Hilfe von

AbstractXmlApplicationContext ctx = new ClasspathXmlApplicationContext("applicationContext.xml");

definieren wir, dass Spring die Datei applicationContext.xml laden soll. Diese muss sich im Java-Classpath befinden.

Wir können aber auch folgendes machen:

AbstractXmlApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");

FileSystemXmlApplicationContext erbt ebenfalls von AbstractXmlApplicationContext und bietet die Möglichkeit, über Prefixe auf Resource zuzugreifen. Das Prefix classpath führt dazu, dass die Datei innerhalb des Classpathes gesucht wird. Das Prefix file hingegen, weist auf eine Suche im Dateisystem hin. Mehr Infos gibt es in der Resources-Abteilung von Spring.

Über die lokale Variable ctx können wir nun unser Bean laden und den Text ausgeben:

IStringify stringifierBean = (IStringify)ctx.getBean("stringifierBean");
System.out.println(stringifierBean.stringify());

ctx.getBean(“stringifierBean”) sucht nun in der XML-Konfiguration nach einem Bean mit genau diesem Namen und erzeugt es.

Unsere Main-Klasse sieht nun folgendermaßen aus:

class Main {
  public static void main(String args[]) {
    // Neuen ApplicationContext laden. Unsere XML-Konfiguration applicationContext.xml wird dabei im Classpath gesucht
    AbstractXmlApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
    // getBean("idDesBeans") lädt nun das Bean. getBean() liefert Object zurück, deshalb der Typecast in das korrekte Interface
    IStringify stringifierBean = (IStringify)ctx.getBean("stringifierBean");
    System.out.println(stringifierBean.stringify());
  }
}

Die XML-Konfigurationsdatei II, Beans

Immer noch am Grübeln, woher denn nun das ominöse stringifierBean kommt? Hier folgt die Aufklärung: Jedes Bean, dass wir innerhalb unseres Java-Codes ansprechen wollen, braucht einen Identifier und/oder Namen. Hinter stringifierBean verbirgt sich nun das Bean, dass wir zur Verarbeitung des Strings benutzen. Ein Bean ist nichts weiter als eine Java-Klasse mit optionalen getter- und setter-Methoden und mindestens einem öffentlichen Konstruktor (auf Factories werde ich jetzt nicht weiter eingehen).

Folgenden Code-Schnipsel packen wir nun in das beans-Element unserer XML-Datei:

  <bean id="stringifierBean" name="stringifierBean" class="StringHandlerSecond" />

Wo ist der Unterschied zwischen den Attributen id und name? Spring behandelt unter 3.3.1 Naming Beans das Thema. Kurz gesagt: id benutzen. Jede ID darf genau einmal vergeben werden!

Mit der obigen XML-Deklaration definieren wir, dass Spring beim Aufruf von getBean(“stringifierBean”) eine Instanz der Klasse StringHandlerSecond zurückliefert. Um hingegen eine Instanz der Klasse StringHandlerBackwards zu bekommen, müssen wir anstatt class=”StringHandlerSecond” class=”StringHandlerBackwards” schreiben.

Singletons, Scopes und Prototypen

Standardmäßig verhält sich Spring so, dass von einem Bean genau immer eine Instanz erzeugt. Nicht zwei, nicht 20, nicht 200 sondern genau eine. Jedes Bean ist somit ein Singleton.

Folgender Code macht keinen Sinn:

IStringHandler stringHandler1 = ctx.getBean("stringifierBean");
IStringHandler stringHandler2 = ctx.getBean("stringifierBean");

ctx.getBean(“stringifierBean”) liefert die selbe Instanz zurück, somit ist stringHandler1 und stringHandler2 das selbe Objekt und wir haben doppelten Code.

Manchmal (oder öfter) ist es aber nötig, dass von einer Klasse nicht nur eine Instanz sondern n-Instanzen existieren können. Spring führt deshalb den so genannten Scope ein. Dieser definiert, was beim Zugriff auf das Bean mit getBean(“beanName”) geschieht. Standardmäßig steht der Scope auf singleton. Um mit jeden Aufruf ein neues Objekt der Klasse zu bekommen, muss der Scope auf prototype stehen. Um das zu erreichen, müssen wir unsere Bean-Definition folgendermaßen anpassen:

  <bean id="stringifierBean" name="stringifierBean" class="StringHandlerSecond" scope="prototype"/>

Das Attribut scope mit dem Wert prototype bemerkt? Gut! Genau das sorgt dafür, dass mit jedem Auf ein neues Objekt erzeugt wird.

Was macht denn Spring nun eigentlich beim Aufruf von getBean()?

Spring geht beim Aufruf von ctx.getBean(“stringifierBean”) folgendermaßen vor:

  • Existiert das Bean stringifierBean in der Konfiguration?
    Nein: Fehler
    Ja: Weiter geht es
  • Hat das Bean einen prototype Scope?
    Ja: Neues Objekt erzeugen
    Nein:: Weiter geht es
  • Existiert das Bean stringifierBean im Cache von Spring?
    Ja: Objekt aus dem Cache zurückliefern
    Nein: Neues Objekt erzeugen, im Cache speichern und Referenz auf das Objekt zurückliefern.

Properties und Konstruktor-Argumente

Momentan macht unsere Applikation noch gar nichts, da der String, der umgewandelt werden soll, noch nicht hinterlegt worden ist.
Wir können nun den String auf zwei Arten injizieren:

  <bean id="stringifierBean" name="stringifierBean" class="StringHandlerSecond">
    <property name="string" value="Mein String" />
  </bean>

Dadurch wird das Objekt von Spring erzeugt (es wird der leere Konstruktor benutzt) und dann dann die setter-Methode setString mit dem Wert “Mein String” aufgerufen.
Intern macht Spring eine Art

StringHandlerSecond stringifierBean = new StringHandlerSecond();
stringifierBean.setString("Mein String");
// addToCache(stringifierBean);
  <bean id="stringifierBean" name="stringifierBean" class="StringHandlerSecond">
    <constructor-arg type="java.lang.String" value="Mein String" />
  </bean>

Hiermit wird der Konstruktor aufgerufen, dessen Parameter vom Typ java.lang.String ist.
Auch hier wieder der Pseudo-Code, der innerhalb von Spring aufgerufen wird (ist natürlich alles etwas komplizierter, zum groben Verständnis soll das aber reichen):

StringHandlerSecond stringifierBean = new StringHandlerSecond("Mein String");
// addToCache(stringifierBean);

Man kann natürlich auch beides kombinieren:

  <bean id="stringifierBean" name="stringifierBean" class="StringHandlerSecond">
    <constructor-arg type="java.lang.String" value="Mein String" />
    <property name="string" value="Und dann überschreibe ich den String" />
  </bean>
StringHandlerSecond stringifierBean = new StringHandlerSecond("Mein String");
stringifierBean.setString("Und dann überschreibe ich den String");
// addToCache(stringifierBean);

Man kann auch mit Hilfe des Attributs ref auf andere Beans verweisen. Bei der Verwendung des Attributs ref ist wichtig, dass das zu referenzierende Bean nicht wieder auf das Bean verweist, von dem referenziert wird. Nicht verstanden?

<bean id="A" name="A" class="AKlasse">
  <property name="andereKlasse" value="B" />
</bean>

<bean id="B" name="B" class="BKlasse">
  <property name="andereKlasse" value="A" />
</bean>

Das funktioniert nicht, da wir in eine so genannte zirkuläre Abhängigkeit geraten. A und B hängen gegenseitig von einander ab. Spring weiß nicht, wie er die Klassen zuerst initalisieren soll.

.properties-Dateien, Einstellungen auslagern

Momentan haben wir den Wert für unseren umzuwandelnden String noch fest in die XML-Datei geschrieben. Das ist natürlich möglich, der Endanwender interessiert sich aber eher nicht so für das ganze XML-Geraffel. Deshalb lagern wir die Variablen nun aus. Dazu speichern wir folgenden Code in der Datei stringifier.properties

stringifier.class=StringHandlerSecond # oder StringHandlerBackwards
stringifier.string=Hallo Wald!

und passen unsere applicationContext.xml an:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:property-placeholder location="stringifier.properties"/>

    <bean id="stringifierBean" name="stringifierBean" class="${stringifier.class}">
      <constructor-arg type="java.lang.String" value="${stringifier.string}" />
    </bean>
</beans>

Mit context.property-placeholder definieren wir eine Konfigurationsdatei, in der die zu ersetzenden Werte im Muster ${} stehen.

Das soll erst einmal reichen. Spring umfasst noch viel viel mehr, sei es Annotations, AOP, Unterstützung von Scriptsprachen u.s.w. – Ich kann nur empfehlen, sich die hervorragende Dokumentation durchzulesen. Sie ist meiner Meinung nach eine der besten Dokumentationen, die im Internet zu finden ist.

History of the Internet

Ich bin gerade über das Video History of the Internet gestolpert – meiner Meinung nach ein Video, was jeder angehende Fachinformatiker gesehen haben sollte.

Das Video benutzt hauptsächlich IT-spezifische Piktogramme, die im Zuge einer Diplomarbeit enstanden. Durch die Benutzung der Piktogramme ist es für den Zuschauer wesentlich einfacher, sich die Fakten zu merken.

Infos zu den Piktogrammen und der Arbeit gibt es unter http://www.lonja.de/diploma/index.html.

Ausbildung: Subnetting

Auf youtube gibt es ein paar gute Videos mit der Einführung zum Thema Subnetting:

httpv://de.youtube.com/watch?v=SbjjAXJBSQc
httpv://de.youtube.com/watch?v=AkJ5gOitmLo
httpv://de.youtube.com/watch?v=tqr0QFC0aLo
httpv://de.youtube.com/watch?v=ulxWHkaoaw8

Insgesamtdauer der Videos: knapp 40 Minuten und alles in deutsch gehalten. Sehr lohnenswert und gut dargestellt.