Die Hitparade der Java-Microframeworks: Ein Blick auf Spark, Ninja, Jodd und Ratpack

Microservices machen es deutlich: Der Trend geht zurück zu Spezialisierung und kleinen, überschaubaren Lösungen. Die neue Klasse der Microframeworks setzt das fort und verspricht neue Eleganz für die Java-Welt. Ein Blick auf vier Kandidaten zeigt, ob sie das Versprechen halten.

In Pocket speichern vorlesen Druckansicht 9 Kommentare lesen
Lesezeit: 16 Min.
Von
  • Benjamin Schmid
Inhaltsverzeichnis

Microservices machen es deutlich: Der Trend geht zurück zu Spezialisierung und kleinen, überschaubaren Lösungen. Die neue Klasse der Microframeworks setzt das fort und verspricht neue Eleganz für die Java-Welt. Ein Blick auf die vier Kandidaten Jodd, Spark, Ninja und Ratpack zeigt, ob sie das Versprechen halten.

Java und sein Ökosystem glänzen durch eine Jahrzehnte überdauernde Abwärtskompatibilität bei exzellenter Skalierbarkeit und Stabilität. Auf der Kehrseite der Medaille sammelten sich über die Zeit viele alte und lästige Zöpfe an. Weniger Ballast und mehr Produktivität durch Konzentration auf das Wesentliche bestimmen daher den Zeitgeist neuerer Ansätze. Zu den ersten Vertretern dieser integrierten und fokussierten Baukästen gehörten Dropwizard oder Spring Boot; beide mit dem Ziel, in kürzestmöglicher Zeit produktionsreife Java-Anwendungen entwickeln zu können.

Während viele Bibliotheken von Release zu Release wachsen sowie für einfache Projekte unnötige Funktionen und Ballast anhäufen, üben sich Microframeworks im stringenten Minimalismus. Ihre Idee ist es, elegante, flexible und auf ein Thema fokussierte Rahmenwerke für eine Problemstellung – in der Regel Webanwendungen – anzubieten. Insbesondere in Kombination mit Java 8 erlauben sie den Entwicklern neue Prägnanz im Code.

Am Beispiel einer kleinen Webanwendung zur Verwaltung von Einkaufslisten im Architekturmuster einer RESTFul-Applikation sei ein genauerer Blick auf vier Kandidaten geworfen. Ausgewählte Code-Ausschnitte bieten erste Eindrücke in das jeweilige Framework. Für ein vollständigeres Bild bietet sich das Studium der Beispielimplementierungen an, die auf GitHub zum Ausprobieren und Experimentieren einladen.

Java-Microframeworks (5 Bilder)

Kleine Anwendung

Eine kleine Anwendung zum Anlegen, Pflegen und Lesen von Einkaufslisten dient als Grundlage für den Vergleich.


Das nicht mit seinem Apache-Namensvetter zu verwechselnde Spark hat seine spirituellen Wurzeln im Ruby-Framework Sinatra und konzentriert sich auf Webanwendungen mit Java 8 bei minimalem Aufwand. Der Vorlage entsprechend sind Routen das zentrale Strukturierungsmittel seiner DSL-artigen Beschreibungssprache. Über sie werden HTTP-Anfragen auf die passenden Callbacks umgeleitet, die dann die Verarbeitung und Beantwortung des Requests übernehmen. Wie elegant und prägnant das dank gebündeltem Jetty-Server und Java-8-Lambda-Ausdrücken gelingt, demonstriert das erste Minimalbeispiel. Trotz seiner Kürze ist es bereits vollständig und ohne weiteres Deployment direkt lauffähig.

import static spark.Spark.*;

public static void main(String[] args) {
get("/hello", "text/html", (req, res)
-> "<!DOCTYPE html><html><h1>" +
"Hello World</h1></html>");

get("/hello", (req, res) -> "Hello World");

get("/hello/:name", ((req, res)
-> "Hello " + req.params("name")));
}

Das Beispiel zeigt die Sinatra-typische Struktur einer Routen-Definition als Abbildung eines HTTP-Verbs (GET, POST[i], [i]DELETE etc.) und URL-Pfades auf eine Callback-Implementierung. Über die mitgelieferten Request- und Response-Objekte haben Entwickler im Callback ungefilterten Zugriff zum Beispiel auf die Pfad- oder Request-Parameter, die aktuelle HTTP-Session und Cookies oder den gewünschten HTTP-Statuscode der Antwort.

Belange wie eine sinnvolle Zergliederung der Logik, Fehlerbehandlung oder eine effiziente Art, dynamische Antwortseiten zu erzeugen, überlässt Spark vollständig den Entwicklern. Für Letzteres bietet es jedoch eine Armada an Integrationsmodulen für insgesamt neun Template Engines zur Auswahl an. Von Freemarker über das Urgestein Velocity bis hin zu Mustache oder Jade4J ist vieles
vorhanden, was man benötigt, um vorlagenbasiert dynamisch generierte Antworten zu rendern.

get("/hello/:name", "text/html", (req, res) -> {
Map<String, String> values = new HashMap<>();
values.put("name", req.queryParams("name"));
return new ModelAndView(values, "greet.tpl");
}, new JadeTemplateEngine());

Die modellhafte Beispielanwendung einer Einkaufszettel-Verwaltung setzt jedoch auf eine Single-Page-Benutzeroberfläche auf Basis von AngularJS und JavaScript. Dank ng-admin ist damit der Client in nur 65 Zeilen komplett implementiert. Der erforderliche Aufwand für die GUI beschränkt sich also auf das Ausliefern einer rein statischen index.html. Somit genügt ein knappes staticFileLocation("/gui");, um in Spark die Dateien im Paket gui auch an den Browser auszuliefern.

Das verbleibende Problem liegt somit bei der Implementierung einer REST API, über welche die GUI den lesenden und schreibenden Zugriff auf die Einkaufslisten erhält. Nachdem Spark auch hier alle Freiheiten offen lässt, entscheidet sich der Autor für zwei hilfreiche Ergänzungen: Lombok zum Generieren der Getter, Setter und equals()-Methoden in den Modellklassen und Jackson zum Konvertieren der Datenobjekte in das JSON-Format. Die Definition und der Datenaustausch zwischen Datenmodell und REST-Schnittstelle gelingen damit ähnlich prägnant wie die bisherigen Teile.

@lombok.Data
public class GroceryList {
private UUID id;
private List<Item> shoppingItems;
private String comment;
private Date date;
private boolean settled;
}

public class ModelSerializer {
private ObjectMapper mapper = new ObjectMapper();

public GroceryList deserialize(String jsonText)
throws IOException {
return mapper.readValue(jsonText, GroceryList.class);
}

public String serialize(GroceryList groceryList)
throws IOException {
return mapper.writeValueAsString(groceryList);
}
}

Für die vollständige Einkaufslistenverwaltung gilt es nun, analog zum Eingangsbeispiel die REST-Endpunkte über Service-Methoden mit dem Modell und einer Persistenz zu verdrahten.

Die bislang illustrieren Funktionen zeigen fast alle Kernbestandteile von Spark und belegen seine Übersichtlichkeit bei zugleich hohem Nutzwert. Nicht betrachtet wurde lediglich die Funktion, sich über Filter und Transformer in die Verarbeitungskette der Ein- und Ausgangsdaten einzuhängen, und die Möglichkeit, auch für Exceptions Routen zu definieren.