Spring Context and Dependency Injection

Überblick Spring Context and Dependency Injection

Mit Spring Context and Dependency Injection wurde 2003 ein Framework eingeführt, der JEE damals in vielen Bereichen überlegen war. Die grundsätzlichen Ideen wurden zwar größtenteils in den aktuellen Versionen Spezifikation nachgezogen, trotzdem bleibt Spring bis heute umfangreicher. Dieser Vorteil wird zwar durch eine Bindung an ein proprietäres Framework bezahlt, aber viele Architekten sind bereit, diese Konsequenz zu akzeptieren.

Spring Context and Dependency Injection unterstützt drei unterschiedliche Technologien:

  • XML
  • Annotations
  • Java Config

Diese können jederzeit untereinander beliebig gemischt werden.

XML

Die Definition der CDI-Objekte mit XML war der ursprüngliche Ansatz, der von der Spring Community gewählt wurde. Hierzu werden die Spring Beans in einer XML-Datei abgelegt:

Mit XML können Anwendungen sehr elegant und kompakt definiert werden. Allerdings wird zur Unterstützung eine komplexe IDE wie IntelliJ oder die Spring Tools Suite benötigt. Sonst werden elementare Operationen der Software-Entwicklung wie Refactoring oder Code-Assistenten nicht unterstützt.

Annotations

Mit der Einführung der Annotationen mit Java 5 wurde die Definition der Spring Beans auch direkt aus dem Java Code heraus möglich. Dazu wurde ein Satz von Spring-spezifischen Annotations-Typen eingeführt:

  • @Component
  • @Service
  • @Repository

Diese Typen sind im stereotypes-Paket definiert und ermöglichen deshalb eine sinnvolle Gruppierung und Dokumentation der Anwendungs-Klassen. Prinzipiell würde die@Component-Annotation genügen.

Die Annotations-basierte Definition ist bei den Anwendungs-Programmieren sehr beliebt und wird in vielen Projekten bevorzugt benutzt.

Java Config

Dies ist der bisher neueste Ansatz, Spring Beans zu definieren. Hierzu wird eine mit @Configurationannotierte Klasse eingeführt, welche mit@Beanannotierte Factory-Methoden enthält. Der Spring-Kontext instanziert die diese Klasse und ruft diese Method je nach Bedarf auf. Wichtig ist hierbei, dass die @Configuration-Klasse sowie die Factory-Methoden selber ebenfalls wieder Dependencies aufweisen dürfen. Ein einfaches Beispiel ist hier noch gegeben:

Java Config scheint zwar ein Rückschritt zu sein, da nun ja wieder der Entwickler selber den Lebenszyklus der Fachobjekte übernimmt. Allerdings stehen auch hier die weiteren Spring-Features zur Verfügung, so dass dieser Ansatz eine durchaus sinnvolle Ergänzung ist.

Zusammenspiel mit Spring Boot

Spring Context and Dependeny Injection wird selbstverständlich komplett von Spring Boot unterstützt. Ohne weitere Konfiguration werden automatisch alle Pakete unterhalb und inklusive des Application-Hauptpakets nach Klassen mit Spring-Annotationen durchforstet und analysiert. Für das Einbinden einer XML-Konfiguration kann @ImportResource benutzt werden.

 


Seminar zum Thema

Weiterlesen

Konfiguration von Spring-Anwendungen

Konfiguration von Spring-Anwendungen

Die Konfiguration von Spring-Anwendungen ist ein elementarer Bestandteil des Frameworks. Damit können auch komplexeste Anforderungen in produktiven Anwendungen umgesetzt werden.

Berücksichtigt werden dabei die folgenden Features:

  • Die eigentliche Konfiguration der Anwendung
  • Die Konfiguration der Laufzeitumgebung sowie deren Überwachung
  • Die Unterstützung verschiedener Profile der Anwendung.

Konfiguration der Anwendung

Bei Spring Boot-Anwendungen wird hierzu automatisch eine Konfigurationsdatei geladen. Dabei werden simple Properties-Dateien (die application.properties)   oder YAML-Dateien (application.yml) unterstützt.

Innerhalb der Anwendung werden Properties mit der @Value-Annotation definiert. Diese lesen über eine Spring Expression einen Wert aus der Konfigurationsdatei:

Profiles

Mit Profiles werden unterschiedliche Stages der Anwendung konfiguriert. Dabei kann jegliche Konfiguration (XML, Annotations oder Java Config) zusätzlich mit der @Profile-Annotation einem benannten Profil zugeordnet werden.

Profile werden innerhalb der Konfiguration oder durch einen Aufruf-Parameter der Anwendung aktiviert.

Die oben bereits angesprochenen Konfigurations-Dateien werden automatisch mit Profiles ergänzt. Existiert beispielsweise ein Profile namens „test“, so wirde neben der application.properties-Datei auch eineapplication-test.propertiesgelesen.

Konfiguration der Laufzeitumgebung

Auch die Laufzeitumgebung der Anwendung kann konsequent in der application-Konfiguration erfolgen. Beispiele hierfür sind

  • Logging
  • Datenbank-URLs
  • Server-Ports
  • Aktive Profile

Im Folgenden Beispiel ist eine Konfiguration für Logging gegeben. Weiterhin wird das aktive Profile gesetzt:

 

Setup für Test-Datenbanken

Wird eine Spring-Boot-Anwendung mit einer Datenbank-Anbindung konfiguriert, so können automatisch Skripte zur Schema-Erstellung sowie zum initialen Befüllen der Tabellen ausgeführt werden. Diese Dateien sind die schema.sql bzw. die data.sql.

Beispiel: Eine schema.sql

Beispiel: Eine data.sql

Auch hier ist wiederum eine Unterscheidung verschiedener Stages möglich. Dies wird jedoch nicht über ein Profil geregelt sonder durch den Eintrag einer Platform in der Anwendungs-Konfiguration. Die folgende Konfiguration zeigt eine komplette Datenbank-Konfiguration für eine Test-Datenbank:

 

Gelesen werden dann die beiden Dateienschema-test.sqlund data-test.sql.

Der Spring Konfigurationsserver

In einer Cloud-basierten Lösung können die Konfigurationsdateien von einem zentralen Server, dem Spring Cloud Config Server, geliefert werden. Die Standard-Implementierung des Spring-Cloud-Projekts liest die Konfigurationswerte einfach aus einem Git-Repository. Werden Änderungen in die Versionsverwaltung gepushed so wird damit die Anwendung neu konfiguriert. Eine wirklich interessante Lösung! Selbstverständlich können die Konfigurationen aber auch klassisch in einer Datenbank oder dem Dateisystem abgelegt werden.


Seminar zum Thema

Weiterlesen

Spring Data

Spring Data

Spring Data ist ein zentrales Projekt des Spring Frameworks und besticht durch seine Einfachheit und Konsistenz. So können selbst komplexe Datenabfragen mit wenigen Zeilen an Quellcode erstellt werden. Insbesondere im Zusammenspiel mit Spring Boot ist es so möglich, mit wenigen Programmzeilen eine komplette Anwendung wie beispielsweise einen Restful WebService zu realisieren.

Eine Übersicht

Der Begriff „Datenabfragen“ ist im Übrigen nicht zufällig so allgemein gewählt: Spring Data selbst definiert nur eine relativ schlanke Abstraktionsschicht auf einen Datenbestand. So besitzen alle Datenobjekte eine eindeutige ID und werden über ein Repository mit  CRUD-Operationen verwaltet.

Alle weiteren Details sind abhängig von der konkret verwendeten Datenbank-Technologie. So unterscheidet sich selbstverständlich ein konkretes JPA-Repository mit einer transaktionsfähigen relationalen Datenbank im Hintergrund von einem Mongo-Repository, bei dem eine Dokumenten-orientierte Datenbank aus dem NoSQL-Umfeld angesprochen wird.

Spring Data Projekte

Die Spring-Entwickler haben damit nicht den Fehler gemacht zu versuchen, jegliche Datenzugriffs-Technologien allgemein zu abstrahieren. Dieser Versuch ist mit der allgemeinen JEE-Connector-Spezifikation bereits misslungen. Statt dessen gruppiert Spring Data verschiedene Implementierungen mit durchaus unterschiedlichen APIs unter einem gemeinsamen Namen.

Die Anzahl der Projekte ist selbst in der offiziellen Spring-Distribution bereits sehr umfassend und wird durch die Community permanent weiter ergänzt.

Gemeinsam ist all diesen Teilprojekten jedoch die grundsätzliche Arbeitsweise: Entitäten werden über ein dynamisch generiertes Repository im Storage abgelegt.

Ein einfaches Beispiel mit Spring Data JPA

Diese Arbeitsweise wird im folgenden Beispiel näher erläutert:

Wir beginnen hierzu mit einer einfachen Daten-Entität:

Zu beachten ist hierbei bereits, dass diese Entität die normalen Annotationen des Java Persistence APIs benutzt. Insbesondere kann @javax.persistence.Id benutzt werden, die Verwendung von @org.springframework.data.annotation.Id ist optional.

Das Repository selber ist für eine einfache Anwendung komplett trivial:

Die Methoden des CrudRepository werden von Spring zur Laufzeit automatisch generiert, so dass das Repository ohne jegliche eigene Programmierung injiziert und sofort benutzt werden kann. Es ist nicht notwendig, das umfangreiche API zu implementieren! Zur Laufzeit generiert Spring hierfür einen generischen Proxy, der die Aufrufe in geeigneter Form an den gekapselten JPA-EntityManager delegiert.

Dies ist übrigens keine Prototyp- oder Dummy-Implementierung! Es ist weder notwendig noch gebräuchlich noch sinnvoll, für den  produktiven Einsatz diese Methoden durch eigene Sequenzen zu ersetzen und damit zu „verbessern“.

Genügen die im Basis-Interface definierten Methoden nicht, können diese durch eigene ergänzt werden. Hierbei werden folgende Möglichkeiten bereitgestellt:

  • Namenskonventionen: Eine Methode mit der Signatur findByLastnameOrderedByHeight(String lastname) kann einfach im Interface deklariert werden. Eine Implementierung ist nicht notwendig, da das im Endeffekt zu generierende JQL-Statement wohl offensichtlich ist.
  • Sollen diese Namenskonvention nicht verwendet werden, kann statt dessen eine Methode beliebiger Signatur mit der @Query-Annotation versehen werden.

    Statt der Annotationen können auch Named Queries der orm.xml benutzt werden.
  • Und schließlich kann das Interface in einer abstrakten Klasse selbst programiert werden. Dabei werden aber selbstverständlich nur die besonders zu behandelnden Fälle implementiert.

Seminar zum Thema

Weiterlesen

DockerDuke

Docker und Java – Teil 4: Ein RESTful Web Service mit Spring Boot

Im letzten Teil dieser Serie programmieren wir einen RESTful Web Service mit Spring Boot und stellen diesen über ein Docker-Image zur Verfügung. Dabei stützen wir uns auf den im dritten Teil beschriebenen Build-Prozess.

Spring

Was ist Spring Boot?

Mit Hilfe von Spring Boot können auch komplexe Server-Anwendung als einfaches Java-Archiv ausgebracht und gestartet werden. Dazu werden durch einen ausgefeilten Build-Prozess die Anwendungsklassen zusammen mit allen notwendigen Server-Bibliotheken in ein einziges ausführbares Java-Archiv gepackt.

Der Build-Prozess selbst wird wie üblich mit Hilfe eines Maven-Parents definiert. Dieser wird von der Spring-Community zur Verfügung gestellt.

Benutzen wie dieses POM als Parent für das im 3. Teil der Artikelreihe benutzten Parents, so haben wir den Docker- und den Spring-Boot-Build vereint. Mehr ist tatsächlich nicht zu tun! Im  folgenden ist die vollständige Parent-POM dieser Anwendung gegeben, ergänzt um die (hier noch nicht benutzten) Abhängigkeiten zu Spring Data JPA und einer MySQL-Datenbank.

 

Ein RESTful Web Service

RESTful Web Services werden in Java meistens mit Annotationen realisiert. Dabei wird eine URL auf eine Java-Methodensignatur abgebildet. Dies erfolgt meistens durch Annotationen, entweder mit JAX-RS oder mit den RequestMappings aus Spring-MVC. Im folgenden Beispiel benutzen wir den zweiten Ansatz:

 

Diese Java-Klasse kann aus Eclipse heraus sofort gestartet werden und anschließend beispielsweise mit Hilfe des curl-Kommandos getestet werden:

curl -X GET http://localhost:8080/echo/Hello

Ein Maven-Build des Projekts erzeugt das Java-Archiv bzw. lädt es in das Artefakt-Repository

mvn package
mvn deploy

Ein Aufruf ist dann unter der Angabe des Java-Archivs möglich:
java -jar org.javacream.training.rest.spring.basic-0.1.0.jar

Natürlich kann das Archiv auch unbenannt werden, dann ist der Aufruf noch einfacher:
java -jar app.jar

Wichtig ist hier, dass das gebildete Artefakt alle notwendigen Bibliotheken mitbringt, um den Web Server für die http-Requests zu starten. Das Archiv ist vollständig.

Der Docker-Build

Zum Erstellen des Docker-Images brauchen wir nun nur noch ein Dockerfile! Dessen Inhalt ist aus den vorherigen Ausführungen heraus allerdings schon fast trivial:

  1. Als Basis nehmen wir eine Java-Grundinstallation.
  2. Dann kopieren wir noch das generierte Artefakt und
  3. definieren als EntryPoint den Java-Aufruf.
  4. Eine Port-Mapping oder ein Mounten des Log-Directories des Containers ist selbstverständlich noch möglich.

Der RESTful Web Service wird nun ganz normal gestartet:

docker run --rm javacream:org.javacream.training.rest.spring.basic:0.0.1

und könnte wieder über curl getestet werden.

Soll das Image noch auf das Artefakt-Repository geschoben werden genügt ein

mvn docker:push

Das funktioniert aber natürlich nur, wenn ein eigenes Repository betrieben wird. Aber auch dieses Problem ist bereits gelöst: Nexus und Artefactory unterstützen Docker ganz analog zu Java-Artefakten.

Damit steht das Image anderen Entwicklern, der Test&QS-Abteilung oder den System-Administratoren der Produktionsumgebung zur Verfügung.


Dieser Artikel ist der Abschluss meiner Reihe über „Docker und Java“. Wer mehr über den Einsatz von Java in einer Microservice-Systemarchitektur lesen möchte: Im Frühjahr 2017 erscheint eine Reihe von Artikeln zum Thema „Microservice-Architekturen mit Docker“.


Seminare zum Thema

 

Weiterlesen