Über unsMediaKontaktImpressum
Michael Simons 08. Mai 2018

4 Jahre Spring Boot

Spring Boot fiel mir zum ersten Mal Ende 2013 auf, ungefähr vier Monate vor dem 1.0 Release am 1. April 2014. Schon wieder ein neues Projekt im Spring-Ökosystem? Die nächste Variante, eine Spring-Anwendung zu bauen? Oder etwas, das explizit auf Microservices abzielt?

Das Spring-Framework wurde 2002 erstmals als Idee vorgestellt und ein Jahr später unter dem Namen Spring-Framework als quelloffenes Projekt veröffentlicht. Das Ziel – damals wie heute – ist, die Entwicklung mit Java zu vereinfachen und gute Programmierpraktiken zu fördern. Spring Boot ist in diesem Kontext kein neues Framework, sondern eine umfassende Lösung, die Abhängigkeitsmanagement, Build und vieles andere erheblich vereinfacht.

Hintergründe

Spring Boot unterscheidet sich deutlich von anderen – 2014 verfügbaren – Frameworks, die auf Basis des Spring-Frameworks einen schnellen Entwicklungsstart versprachen: Das in Groovy geschriebene Grails-Framework setzte maßgeblich – inspiriert von Ruby on Rails – auf "Convention over configuration" sowie in Teilen auf Code-Generierung, Spring Roo auf Code- und Strukturgenerierung und eine strikte Vorgabe von Technologien.

Spring Boot verfolgt einen anderen Ansatz, der sich aus zwei Kernzielen entwickelt: Schneller Start der Entwicklung mit Spring sowie sinnvolle Defaults, die schnell aus dem Weg gehen, wenn sie nicht mehr benötigt werden. Grundlage dieser Mechanismen sind die sogenannten Starter. Module, die die Verwaltung von Abhängigkeiten sowie automatische Konfiguration übernehmen. Der Mechanismus mag magisch anmuten, der Autor ist da anderer Meinung.

Spring Boot ist ein "opinionated framework", ein Werkzeug mit eigener Meinung. Ähnlich wie mit Ruby on Rails, Grails oder Roo werden aus einer gewissen Menge Annahmen Aktionen und Verhalten abgeleitet. Allerdings passt in meinen Augen die Übersetzung "Vorstellung" besser, denn Spring Boot hat eine Vorstellung davon, wie Dinge strukturiert werden sollten, forciert diese aber in der Regel nicht. Das Team hinter Grails hat übrigens mit Grails 3 ebenfalls auf Spring Boot gesetzt.

Was können Sie mit Spring Boot machen?

Ich lese oft etwas über "das Microservice-Framework Spring Boot" und schüttele immer noch verwundert den Kopf: Spring Boot ist kein Microservice-Framework. Sie können sehr gut und effektiv Microservices mit Spring Boot schreiben. In der Regel sind die sogar recht effizient, aber Spring Boot taugt zu viel mehr. Spring Boot eignet sich hervorragend, um modulare Monolithen zu entwickeln [1].

Meiner Meinung nach sollten neue Spring-Projekte mit Spring Boot umgesetzt werden, unabhängig, ob es sich um Microservices, Monolithen oder auch um Function-as-a-Service-Projekte handelt.

Alltagswerkzeug oder Neuland?

2018 noch einen Text über Spring Boot zu schreiben stellt mich vor Herausforderungen. Anfang März erschien mit Version 2 das erste, große Update seit Spring Boot 1.x. Die Entwicklung hat über ein Jahr in Anspruch genommen. Intern wurde gefühlt kein Stein auf dem anderen gelassen, nach außen hin sind die meisten Schnittstellen und Verhaltensmuster gleich geblieben.

Ist es sinnvoll, eine Liste neuer Features aufzuschreiben? Im Grunde genommen stehen diese in den Release-Notes, zusammen mit den neuen Versionen der von Spring Boot verwalteten Abhängigkeiten [2]. Die finde ich in Teilen erwähnenswerter als Änderungen in Spring Boot. Bei der Aktualisierung einer Spring-Boot-1.x-Anwendung ist insbesondere auf Änderungen in Spring Data und Spring Security sowie im Spring-Actuator-Modul zu achten.

Der Neueinstieg in die Spring-Entwicklung gelingt meines Erachtens sehr gut über die offizielle Dokumentation und entsprechende Guides [3]. Mein eigenes Projekt, das aktuelle Spring-Buch, nimmt den Leser auf dem Weg von den Grundlagen hin zu reaktiver Datenverarbeitung an die Hand [4].

Spring Boot ist eine Erfolgsgeschichte: In der Java-Szene werden Sie aktuell vermutlich nicht daran vorbeikommen. Und wie das mit Erfolgsgeschichten so ist: Auf der einen Seite gibt es viel Zustimmung, auf der anderen Seite viel Ablehnung.

Ich kann es gut verstehen, dass niemand mehr Anwendungen sehen möchte, in denen alle Konfigurationsmöglichkeiten einer Spring-Anwendung (XML, implizierte Suche nach Komponenten über Annotationen in allen möglichen Layern der Architektur und explizite Konfiguration vermengt werden), aber ebenso gut möchte ich das Thema auch nicht mehr in jedem Alltagsprojekt neu diskutieren. Das gleiche gilt für den generellen Einsatz von Annotationen.

Spring Boot ist vier Jahre alt, Spring selber über 15, daher fällt es schwer, es als "Hype-Technologie" zu bezeichnen. Diesen Anspruch hat es aber auch nicht. Gleiches gilt für die Wahl von Programmiersprachen: Spring unterstützt Java, Groovy und Kotlin auf der JVM out-of-the-box, Kotlin sogar mit vielen Erweiterungen. Unter anderem gibt es hier eine eigene domainenspezifische Sprache (Domain Specific Language, DSL) zur Deklaration von Beans. Spring hat aber nicht den Anspruch, rein funktionale Programmierung auf Basis von Clojure, Scala oder ähnliches zu vereinfachen.

In meinen Augen sind Spring und Spring Boot perfekte Werkzeuge, um sich im Projektalltag auf die Dinge zu konzentrieren, die wirklich wichtig sind: Fachliche Anforderungen. Mit einem durchdachten Schnitt, sowohl inhaltlicher als auch technischer Natur werden Sie feststellen, dass in Ihren fachlichen Klassen keine Spring-spezifischen Konstrukte auftreten.

Wege in die Zukunft: Springs reaktive Story

Java EE beziehungsweise neuerdings Jakarta EE ist ein Dejurestandard für die Java Enterprise-Entwicklung. Dieser Standard hat auch Spring beeinflusst. Nicht ohne Grund beziehen sich Spring-Releases in der Regel auch auf eine Java EE-Version. Umgekehrt gilt dasselbe. Spring als Defaktostandard beinflusst natürlich auch Java EE. Spring war in vielen Aspekten ein Vorreiter und könnte es auch mit der reaktiven Story in Spring Boot 2 beziehungsweise Spring Framework 5 wieder sein.

Reaktive Programmiermodelle behandeln die Frage nach effizienter Nutzung von Ressourcen, insbesondere von Threads. Reaktive Programmierung ist nicht nur relevant in Bezug auf die Verarbeitung von HTTP-Anfragen, sondern auch hinsichtlich Datenbanken und ähnlichen Datenquellen: Immer da, wo eine große Menge von Threads über längere Zeit blockiert sein kann oder ein Konsument mit einer am Stück gelieferten Datenmenge überfordert ist, kann reaktive Programmierung helfen.

Anwendungsszenarien können Sie anhand von Infrastrukturanforderungen ableiten und sich dabei an Skalierbarkeit und Stabilität orientieren: In allen Szenarien, in denen viele gleichzeitige und heterogene Verbindungen abgearbeitet werden müssen oder viele Microservices synchron miteinander kommunizieren müssen, könnte eine reaktive Verarbeitung von Daten angezeigt sein. Event-orientierte Anwendungen, die deklarativ einen Eventfluss definieren wollen, sind ein weiterer Anwendungsfall.

Nun ist reaktive Programmierung keine neue Idee. Frameworks wie Akka, Vert.x oder Lagom sind Vorreiter [5].

Spring auf der anderen Seite hat den Vorteil, eine in vielen Unternehmen etablierte Basistechnologie zu sein und eine entsprechende Nutzerbasis zu haben. Spring Boot ist eine innovative, aber aufgrund der seriös gereiften Codebasis, dem Spring Framework, auch eine zugleich konservative Lösung. Mit Spring 5 hält eine Zweiteilung ins Framework Einzug: Parallel zu bestehenden Konzepten werden gleichartige, reaktive Schnittstellen eingeführt. Besonders offensichtlich ist das im Kontext des Spring-Web-MVC-Moduls. Dieses Modul wird in der Regel dazu genutzt, Web- oder REST-Anwendungen zu entwickeln.

Gegeben sei eine Spring Boot-Anwendung, die org.springframework.boot:spring-boot-starter-webflux als Abhängigkeit in ihrem Build-Deskriptor hat. Sie finden diese Anwendung im GitHub-Repository des Artikels [6]. Einen REST-Controller, der eine Liste von Todos zurückgibt, schreiben sie damit wie in Spring-Web-MVC wie gewohnt:

StandardRestController.java.

@RestController
class StandardRestController {
    @GetMapping("/blockingTodos")
    public List<Todo> getTodos() {
        return List.of(
            new Todo("Write article", LocalDate.now()),
            new Todo("Enjoy weekend", LocalDate.now().plusDays(2))
        );
    }
}

Dieser Controller funktioniert sowohl auf Basis des bestehenden Servlet-Stacks als auch innerhalb des reaktiven Stacks. Beide Module teilen sich die bekannten Framework-Annotationen.

Eine nicht-blockierende, reaktive Datenquelle vorausgesetzt, können Sie diesen Controller problemlos in einen reaktiven REST-Endpunkt umschreiben, der dann auf Basis von HTTP/Reactive-Streams per Default auf Netty, aber auch innerhalb der aktuellen Tomcat-, Jetty- und Undertow-Versionen läuft.

Eine erste Variante könnte so aussehen:

ReactiveRestController.java.

@RestController
class ReactiveRestController {
    @GetMapping(value = "/reactiveTodos", produces = APPLICATION_STREAM_JSON_VALUE)
    public Flux<Todo> getTodos() {
        return Flux.just(
            new Todo("Write article", LocalDate.now()),
            new Todo("Enjoy weekend", LocalDate.now().plusDays(2))
        ).delayElements(Duration.ofSeconds(1));
    }
}

Der Unterschied ist der Rückgabetyp: Flux. Ein Flux repräsentiert einen nicht blockierenden Datenstream mit 0 oder n Elementen. Er stammt aus Project Reactor [7] und repräsentiert eine mögliche Implementierung des Publishers der JDK 9 Flow API. Der Datenfluss dieses Controllers fängt erst an, wenn ein Client – der übrigens nicht "reaktiv" sein muss – ein erstes Element verarbeiten kann. Die künstliche Verzögerung mittels delayElements(Duration.ofSeconds(1)) dient nur der Verdeutlichung der Steuerung eines Publishers. Sie werden sie bemerken, wenn Sie den Endpunkt zum Beispiel mittels cURL aufrufen: curl localhost:8080/reactiveTodos.

Bis hierhin haben Sie und Ihr Team bestehendes Wissen trivial weiter nutzen können: Der reaktive Controller nutzt bestehende Mechaniken aus und ergänzt diese um neue Konzepte.

Für mich ist das eine Stärke des Spring-Frameworks: Bestehende Mechanismen werden intelligent genutzt, um sukzessive neue Technologien einzuführen. Natürlich müssen Sie selber wissen, ob Sie für Ihre Domain reaktive Datenverarbeitung und entsprechende Schnittstellen benötigen und wie Sie diese einführen, aber die Lernkurve, um damit programmieren zu können, ist gering.

Gehören Sie zur Fraktion der Menschen, die Annotationen grundsätzlich schlecht finden, haben Sie darüber hinaus jetzt die Möglichkeit, mit Spring auf funktionale Art zu entwickeln:

Funktionales, reaktives Programmiermodell

@SpringBootApplication
public class DemoApplication {
    static RouterFunction<ServerResponse> myRoutes() {
        return route(GET("/functionalReactiveTodos"), request -> ServerResponse.ok()
            .contentType(APPLICATION_STREAM_JSON)
            .body(Flux.just(
                new Todo("Write article", LocalDate.now()),
                new Todo("Enjoy weekend", LocalDate.now().plusDays(2))
            ).delayElements(Duration.ofSeconds(1)), Todo.class)
        );
    }

    public static void main(String... args) {
        new SpringApplicationBuilder()
            .sources(DemoApplication.class)
            .initializers((GenericApplicationContext applicationContext) ->
                applicationContext.registerBean(RouterFunction.class, DemoApplication::myRoutes))
            .run(args);
    }
}

Fallen Sie an dieser Stelle nicht darauf herein, eine "Pyramide des Todes" aus Lambdas zu bauen, sondern nutzen Sie wie im Beispiel Methoden-Handler sowie dezidierte Klassen, die Handler sammeln.

Am Ende liegt es an Ihnen, eine Architektur vorzugeben. Sie können es natürlich so machen, wie ich es in der Demo gemacht habe: Alle Varianten mischen. Sinnvoller erscheint es mir, für Ihre Services sukzessive Paradigmen vorzugeben, und sich damit diesem Thema iterativ zu nähern.

Reaktive Programmierung mit Spring ist übrigens nicht auf REST-Schnittstellen beschränkt. Diese Variante einer Todo-Liste [8] zeigt, wie Sie reaktive Oberflächen mit Spring Boot realisieren.

Fazit

Spring und Spring Boot gehören auch 2018 nicht zum alten Eisen. Bis jetzt scheint das Framework immer noch in der Lage zu sein, auf neue Anforderungen sinnvoll reagieren zu können. In meinen Augen spricht das für Spring. Den Quelltext obiger Beispiele finden Sie auf GitHub [6] und einen Einstieg in die Entwicklung mit Spring sowie fortgeschrittene Themen mit Spring Boot in meinem Buch [4].

Schlussendlich empfehle ich, ein Framework mit Bedacht auszuwählen. Falls Sie sich in einer Situation befinden, in der Sie entweder aus realen technischen Gründen oder aber aus Geschmack ständig gegen das Framework und nicht mit ihm entwickeln müssen, sollten Sie wechseln.

Quellen
  1. Informatik Aktuell – C. Iserlohn, T. Schulte-Coerne: Warum es nicht immer Microservices sein müssen
  2. Release-Notes  
  3. Offizielle Dokumentation
    Guides
  4. M. Simons, 2018: Spring Boot 2: Moderne Softwareentwicklung mit Spring 5, dpunkt-Verlag
  5. Akka, Vert.x, Lagom 
  6. GitHub-Repository
  7. Project Reactor
  8. Github: To Do-Liste

Autor

Publikationen

Kommentare (0)

Neuen Kommentar schreiben