Spring an einem einfachem Beispiel (Tutorial)

Klassendiagramm dieses Beispiels

Dieser Beitrag ist Teil einer (Tutorial-) Serie über die Einführung in das Spring Framework und stellt das Spring Framework an einem kleinen einfachem Beispiel vor.

Die Struktur des Projektes

Die verwendeten Frameworks und Werkzeuge sind hier beschrieben. Dieses Beispiel ist eine Konsolenanwendung, die den Service eines Taschenrechners aufruft. In diesem Beispiel werden die folgenden Technologien des Spring Frameworks vorgestellt:

  • Die Definition einer Spring Bean mit einem Interface über die XML Konfiguration des Spring Frameworks.
  • Die Verwendung von Spring Framework Utilities zur Validierung von Aufrufparametern.
  • Die Erstellung eines JUnit-Tests mit der Verwendung von Java Hamcrest.
  • Die Trennung der Test-Implementierung von der Produktiven-Implementierung.
  • Die automatisches Dependency Injection (Autowired) des Taschenrechners.
  • Die Verwendung von Logging.
  • Das Definieren von Spring Beans mit Annotationen und das Auffinden der Spring Beans mit einem Component scan.
  • Das Starten einer Konsolenanwendung.

Im folgendem Bild ist die Projektstruktur dargestellt.

Die Projektstruktur in Eclipse

Die Projektstruktur in Eclipse (© Frank Rahn)

Im folgendem Bild sind die benötigten Bibliotheken dargestellt. Das Maven Plugin wird hier nur für das Auflösen der Abhängigkeiten (Dependency) und das Downloaden der benötigten Bibliotheken verwendet.

Die benötigten Bibliotheken (Dependencies)

Die benötigten Bibliotheken - Dependencies (© Frank Rahn)

Update

Das Maven Skript wurde vervollständigt und kann auch zum Builden ($ mvn clean package) der Anwendung verwendet werden. Mit $ mvn eclipse:clean eclipse:eclipse werden die Konfigurationsdateien für Eclipse neu erstellt – sie sind aber auch noch im Repository. Die Anwendung kann mit $ mvn exec:java ausgeführt werden.

Die Literaturempfehlungen für dieses Beispiel

Der Service eines Taschenrechners

Zunächst wird die Schnittstelle des Services definiert. Diese erhält vorerst zwei Methoden um Zahlen zu addieren. Für eine Einführung in das Spring Framework ist dieser minimale Funktionsumfang dieser Schnittstelle ausreichend.

/**
 * Ein einfach Taschenrechner.
 * @author Frank W. Rahn
 */
public interface Calculator {

    /**
     * Addiere die rationalen Zahlen.
     * @param summands die Summanden
     * @return die Summe
     */
    double add(double... summands);

    /**
     * Addiere die natürlichen Zahlen.
     * @param summands die Summanden
     * @return die Summe
     */
    int add(int... summands);

}

Hier die Implementierung des Services mit den ersten Prüfungen.

  • In der Zeile 19 und 36 wird eine Prüfung mit Mitteln Assert.notNull(summands) des Spring Frameworks durchgeführt. Wenn der Summand null ist, wird die Ausnahme IllegalArgumentException mit dem angegebenen Text als Nachricht geworfen.
/**
 * Eine Implementierung des einfach Taschenrechners.
 * @author Frank W. Rahn
 */
public class SimpleCalculator implements Calculator {

    /**
     * {@inheritDoc}
     * @see Calculator#add(int[])
     */
    @Override
    public int add(int... summands) {
        notNull(summands, "Die Summanden sind null");

        int result = 0;

        for (int sum : summands) {
            result += sum;
        }

        return result;
    }

    /**
     * {@inheritDoc}
     * @see Calculator#add(double[])
     */
    @Override
    public double add(double... summands) {
        notNull(summands, "Die Summanden sind null");

        double result = 0;

        for (double sum : summands) {
            result += sum;
        }

        return result;
    }

}

Diese Modul wird mit einer XML basierenden Konfiguration aufgebaut.

  • In der Zeile 15 wird für den Taschenrechner ein Alias definiert, mit dem der Taschenrechner zusätzlich aufrufbar ist.
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
    ">

    <description>Dieses ist die Konfiguration des Taschenrechners.</description>

    <!-- Den Taschenrechner definieren -->
    <bean id="simpleCalculator"
        class="de.rahn.services.calculator.standard.SimpleCalculator" />
    <alias name="simpleCalculator" alias="calculator" />

</beans>

Der Test mit JUnit

Die JUnit Tests liegen im Source-Verzeichnis src/test mit einer identischen Paketstruktur, wie unter src/java. Im folgendem Quelltext ist ein JUnit Test für die Implementierung des Taschenrechner dargestellt.

  • In der Zeile 17 wird eine Annotationen für die Konfiguration verwendet, diese lädt die Datei SimpleCalculatorTest-context.xml im gleichen Verzeichnis wie die Klasse SimpleCalculatorTest.
  • Durch die Angabe der Annotation in Zeile 20 wird im Attribut calculator durch das Spring Framework eine Instanz der Implementierung des Services injiziert.
  • In der Zeile 35 wird angegeben, dass dieser Test nicht fehlschlägt, wenn die Ausnahme IllegalArgumentException geworfen wurde.
/**
 * Test der Klasse {@link SimpleCalculator}.
 * @author Frank W. Rahn
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class SimpleCalculatorTest {

    @Autowired
    private SimpleCalculator calculator;

    /**
     * Test method for {@link SimpleCalculator#add(double[])}.
     */
    @Test
    public void testAddDoubleArray() {
        double result = calculator.add(1.0, 2.0);
        assertThat("Test der Addition", result, is(3.0));
    }

    /**
     * Test method for {@link SimpleCalculator#add(int[])}.
     */
    @Test(expected = IllegalArgumentException.class)
    public void testAddIntArray() {
        calculator.add((int[]) null);
    }

}

Die folgende Konfigurationsdatei des Tests importiert nur die Konfigurationsdatei des Taschenrechners.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <description>
        Dieses ist die Konfiguration für den Test des Calculators.
    </description>

    <import resource="classpath:/de/rahn/services/calculator/calculator.xml" />

</beans>

Bei diesem Test wurde die Bibliothek Hamcrest verwendet. Diese Bibliothek verbessert die Ausgabe eines Fehlers.

Im folgendem Beispiel wird ein Fehler mit den Standard-Anweisungen von JUnit erzeugt.

        double result = calculator.add(1.0, 2.0);
        assertEquals("Test der Addition", 4.0, result, 0);
java.lang.AssertionError: Test der Addition expected:<4.0> but was:<3.0>
    at org.junit.Assert.fail(Assert.java:93)
    at org.junit.Assert.failNotEquals(Assert.java:647)
    at org.junit.Assert.assertEquals(Assert.java:443)
    at de.rahn.services.calculator.standard.SimpleCalculatorTest.testAddDoubleArray...

Im folgendem Beispiel wird ein Fehler mit den Anweisungen von JUnit in Verbindung mit Hamcrest ausgelöst.

        double result = calculator.add(1.0, 2.0);
        assertEquals("Test der Addition", 4.0, result, 0);
java.lang.AssertionError: Test der Addition
Expected: is <4.0>
     got: <3.0>

    at org.junit.Assert.assertThat(Assert.java:780)
    at de.rahn.services.calculator.standard.SimpleCalculatorTest.testAddDoubleArray...

Die Anwendung mit Logging

Ein Anwendung, die das Ausführen des Taschenrechners ermöglicht.

  • Dabei wird in der Zeile 18 ein Logger mit der Simple Logging Facade for Java (SLF4J) erzeugt, der dann in den Zeilen 31 und 34 verwendet wird.
  • In der Zeile 21 wird vom Spring Framework eine Instanz des Taschenrechners in das Attribut calculator injiziert.
/**
 * Die Anwendung zum Aufrufen des Taschenrechners.
 * @author Frank W. Rahn
 */
@Component
public class Application implements Runnable {

    private static final Logger logger = getLogger(Application.class);

    @Autowired(required = true)
    private Calculator calculator;

    /**
     * {@inheritDoc}
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        // Aufruf des Taschenrechners
        double result = calculator.add(1.0, 2, 3);
        logger.info("Ergebnis 1 = {}", result);

        result = calculator.add((double[]) null);
        logger.info("Ergebnis 2 = {}", result);
    }

}

Die zugehörige XML Konfiguration. Diesmal wird das Package de.rahn.app hierarchisch nach Spring Beans gescannt (Zeile 18). Diese werden durch die Annotation @Component erkannt.

<?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.xsd
        http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
    ">

    <description>
        Dieses ist die zentrale Konfiguration für die Anwendung.
    </description>

    <!-- Scanne das Package nach Spring Beans -->
    <context:component-scan base-package="de.rahn.app" />

</beans>

Der Starter, eine Konsolenanwendung, initialisiert die gesamte Anwendung (Zeile 15) und führt sie aus (Zeile 21).

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Die Klasse zum Starten der Anwendung.
 * @author Frank W. Rahn
 */
public class Starter {

    /**
     * @param args die Argumente der Anwendung
     */
    public static void main(String[] args) {
        // Initialisierung von Spring
        ApplicationContext ctx =
            new ClassPathXmlApplicationContext(
                "/META-INF/spring/context-app.xml");

        // Aufruf der Anwendung
        Runnable service = ctx.getBean("application", Runnable.class);
        service.run();
    }

}

Zum Abschluss die XML Konfiguration zum Einstieg in die Anwendung.

  • Diese ist im Verzeichnis META-INF/spring abgelegt und verweist in den Zeilen 15 und 16 auf die eigentlichen Module.
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
    ">

    <description>
        Dieses ist die zentrale Konfiguration für die Anwendungen.
    </description>

    <!-- Die projektspezifischen Konfigurationen laden -->
    <import resource="classpath:/de/rahn/services/calculator/calculator.xml" />
    <import resource="classpath:/de/rahn/app/application.xml" />

</beans>

Bei anderen Arten von Anwendungen ändert sich der Name der XML Konfigurationsdatei, wie z. B. bei Webanwendungen in context-web.xml. So können mehrere von diesen Konfigurationen parallel nebeneinander liegen. Zusätzlich werden in diesem Verzeichnis allgemeine technische Konfigurationen, wie z. B. Datenbankinformationen, die von mehreren Modulen verwendet werden, abgelegt. Dadurch kann ein hoher Grad an Wiederverwendung erreicht werden.

Zum Abschluss das resultierende Klassendiagramm (UML) zu diesem Beispiel.

Klassendiagramm dieses Beispiels

Klassendiagramm dieses Beispiels (© Frank Rahn)

Der Quellcode und Download des Beispiels

Quellcode ansehen bei GitHub:
Spring an einem einfachem Beispiel

Download einer ZIP-Datei von GitHub:
Spring an einem einfachem Beispiel

Die Maven Befehle

Eclipse Konfiguration neu erzeugen: $ mvn eclipse:clean eclipse:eclipse

Anwendung bauen: $ mvn clean install

Anwendung ausführen: $ mvn initialize exec:exec

Update am 28.09.2012

Es wurden folgende Änderungen an allen Projekten vorgenommen.

  1. Die Projekte wurden aus meinem lokalen Apache Subversion Repository in meine öffentlichen GitHub Repositories verschoben.
  2. Die folgenden typischen Anpassungen an Git wurden an allen Projekten durchgeführt.
    • .directory gelöscht
    • .gitignore hinzugefügt
    • README.md hinzugefügt
    • COPYRIGHT.md hinzugefügt
    • In jedem Repository wurde für jeden Beitrag der Serie ein Branch angelegt.
      • develop-spring-an-einem-einfachen-beispiel
      • develop-spring-mit-aop
      • develop-spring-mit-jpa-und-hibernate
      • develop-spring-mit-einer-einfachen-webanwendung
      • develop-spring-mit-einer-webanwendung-mit-jpa-und-validierung
      • develop-spring-security-mit-einer-webanwendung
      • develop-spring-mit-restful-webservice

Updates von 06.10.2012 bis zum 14.10.2012

Es wurden folgende Änderungen an allen Projekten vorgenommen.

  1. Aktualisierung des OpenJDK auf die Version 1.7.0.
  2. Aktualisierung der Entwicklungsumgebung Eclipse auf die Version 4.2.1.
    Die benötigten Plugins aus dem Eclipse Marketplace:
  3. Aktualisierung der Datei pom.xml:
    • Anpassungen an die OpenJDK Version
    • GitHub Einträge (SCM und URL) hinzugefügt
    • Aktualisierung der Libraries auf aktuellerer Versionen (z. B. Spring Version 3.1.2.RELEASE, Hibernate Version 4.1.7.Final, JUnit Version 4.10, mockito Version 1.9.0, …)
      Die genaueren Versionen bitte aus den jeweiligen pom.xml auf GitHub entnehmen.
Frank Rahn
Letzte Artikel von Frank Rahn (Alle anzeigen)
0 Kommentare

Hinterlasse einen Kommentar

An der Diskussion beteiligen?
Hinterlasse uns deinen Kommentar!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Ihre E-Mail-Adresse wird nicht veröffentlicht. Ihr Kommentar wird verschlüsselt an meinen Server gesendet. Erforderliche Felder sind mit * markiert.

Weitere Informationen und Widerrufshinweise finden Sie in meiner Datenschutzerklärung.