Einstieg in Spring Boot, Teil 10 Projekt Lombok – Codeminimierung mit Annotationen
In diesem Beitrag der Spring-Boot-Reihe stellen wir das Projekt Lombok vor. Es hat nichts mit dem Framework zu tun, arbeitet aber perfekt damit zusammen und verfolgt das gleiche Ziel: Weg von aufgeblasenem Quellcode, hin zu schlanken wartbaren Projekten.
Anbieter zum Thema

In Java greift man auf die Member-Variablen einer Klasse von außen nicht direkt zu. Das würde dem Prinzip der Kapselung widersprechen. Dieses besagt, dass die Variablen einer Klasse vor anderen Klassen versteckt werden und durch entsprechende Methoden – get und set – zur Verfügung gestellt werden.
Der interne Zustand einer Klasse geht den Nutzer nichts an, er nutzt die zur Verfügung gestellte Schnittstelle. Klingt sinnvoll, hat aber auch einen Nachteil: es müssen eine Menge get- und set-Methoden geschrieben werden. Bei Datenobjekten mit vielen Variablen ist das eine Menge langweiliger Arbeit.
Dies muss man glücklicherweise schon lange nicht mehr von Hand machen. Die großen Entwicklungsumgebungen wie Eclipse, Spring Tool Suite (STS) und IntelliJ IDEA ermöglichen das Generieren der Setter und Getter. In der STS findet man das Feature beispielsweise im Menü unter Source > Generate Getters and Setters.
Nach dem Aufruf der Option lassen sich in einem Dialog die gewünschten Variablen auswählen und anschließend die Methoden erzeugen. Das ist auf alle Fälle besser als die Methoden selber zu schreiben, aber auch noch nicht optimal: Die Klasse wird mit „Boilerplate“-Code, also geschwätzigem und sich wiederholendem Code, aufgebläht.
Das erschwert dem Code-Leser das Verständnis und er wird genötigt, sich alles anzuschauen: Wird vielleicht doch irgendwo noch etwas mit der Variable gemacht, bevor sie zurückgegeben wird? Außerdem muss für neu hinzukommende Variablen die Generierung erneut aufgerufen werden. Das muss doch irgendwie eleganter gehen?
Lombok einrichten
Das Project Lombok, ein Annotation-basierter Präprozessor, nimmt sich diesem und einer ganzer Reihe ähnlicher Probleme an und harmoniert bestens mit Spring Boot. Lombok wird von der Homepage des Projekts als Jar-File heruntergeladen und – bei vorhandener Java-Laufzeitumgebung – durch Doppelklick auf die Datei gestartet.
Der sich öffnende Installer sucht nach passenden IDEs und fügt die Lombok-Unterstützung hinzu. Führt die Suche nicht zum Erfolg, kann man den Speicherort der IDE auch von Hand angeben. Ein Projekt, das Lombok nutzen soll, muss zusätzlich noch das Lombok-Jar einbinden. Im POM einer Spring Boot-Anwendung wird dazu folgende Abhängigkeit eingetragen:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
Damit ist Lombok einsatzbereit.
It’s a Kind of Magic
Alle Angaben zur Codegenerierung für Lombok erfolgen mit Hilfe von Annotationen. Zur Erzeugung der Get- und Set-Methoden für die Membervariablen sind das die Annotationen @Getter und @Setter:
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Person {
private String firstname;
private String lastname;
private String email;
}
Den erzeugten Code für die get- und set-Methode sieht man im Quellcode nicht. Er wird beim Kompilieren in den Bytecode eingefügt. Die Klasse bleibt aufgeräumt und schlank. Dass diese dennoch zur Verfügung stehen, lässt sich in STS in der Outline-View überprüfen. Hier findet man Methoden wie getFirstname() oder setLastname().
Die generierten Methoden können ganz normal genutzt werden, als wenn sie im Quellcode vorhanden wären. Auch die Code-Completion findet die Methoden und bietet wie gewohnt das Vervollständigen der Namen an. Aber Lombok kann weit mehr als nur das Generieren der get- und set-Methoden. Mit der Annotation @ToString wird eine sinnvolle toString()-Methode generiert, die alle Membervariablen der Klasse beinhaltet.
Die Ausgabe für ein wie oben definiertes Person-Objekt sieht zum Beispiel folgendermaßen aus:
Person(firstname=max, lastname=mustermann, email=max@mustermann.de)
Die Annotation @EqualsAndHashCode generiert die Methoden equals() und hashcode() basierend auf den Membervariablen. Nur wenn alle Variablen zweier Objekte den gleichen Inhalt haben, liefert die equals()-Methode true zurück.
Objektbau für Fortgeschrittene
Zum Erzeugen von Konstruktoren bietet die Bibliothek gleich drei Varianten: Mit @NoArgsConstructor wird ein Konstruktor erzeugt, der keine Parameter erwartet, also Person(). @AllArgsConstructor dagegen verlangt alle Variablen in der Parameterliste, hier also Person(String, String, String).
Mit @RequiredArgsConstructor lässt sich ein Konstruktor erzeugen, der nur die erforderlichen Variablen enthält. Das sind nicht initialisierte Variablen, die entweder mit final (sogenannte blank finals) oder mit der Lombok-Annotation @NonNull gekennzeichnet sind.
Im folgenden Beispiel wird ein Konstruktor generiert, der firstname (wegen @NonNull) und email (final) als Parameter erwartet. Der ebenfalls mit final markierte lastname ist nicht „blank“ weil er mit einem Wert initialisiert wird:
@Data
@RequiredArgsConstructor
public class Person {
@NonNull
private String firstname;
final private String lastname = "Mustermann";
final private String email;
}
Konstruktoren mit sehr vielen Parametern werden irgendwann unhandlich. Eine beliebte Alternative ist das Builder-Pattern, bei dem das Erzeugen von Instanzen einer separaten Klasse anvertraut wird. Deren Methoden tragen den Namen der Variable, die sie setzen, hier also beispielsweise email(). Jede der Methoden dieser Klasse gibt das gebaute Objekt zurück, so dass sich gleich die nächste Methode für das erhaltene Objekt aufrufen lässt.
Objekte mit vielen Variablen lassen sich so elegant bauen. Und auch hier leistet Lombok gute Dienste: Die Annotation @Builder an der Klasse Person erzeugt einen PersonBuilder, den man durch Aufruf der Methode builder() auf die Person-Klasse erhält. Der PersonBuilder enthält die Methoden firstname(), lastname(), email() und build(). Das folgende Beispiel demonstriert die Verwendung:
Person person = Person.builder()
.firstname("Max")
.lastname("Mustermann")
.email("Max@Mustermann.de")
.build();
Logging mit der Simple Logging Facade
Zu guter Letzt soll hier noch auf die Möglichkeit hingewiesen werden, Lombok eine Logger-Instanz erzeugen zu lassen. Dies geschieht durch die Annotation der entsprechenden Klasse mit @Slf4j:
@Slf4j
@Controller
public class PersonController {
// ...
}
Intern wird dadurch eine Variable namens log von der LoggerFactory erzeugt:
public class PersonController {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PersonController.class);
}
Auch diese Variable sieht man natürlich nicht im eigenen Quellcode, wohl aber in der Outline-View. Sie kann wie gewohnt genutzt werden:
log.info(person.toString());
Über Lombok scheiden sich die Geister. Und ja, es gibt berechtigte Argumente gegen das Werkzeug. Allen voran die Tatsache, dass alle Projektmitarbeiter die Erweiterung in der IDE installieren müssen. Dennoch war und ist Lombok einfach eine Revolution.
Das Projekt erspart eine Menge Arbeit und verschönert und verschlankt den Code ungemein. Im Zusammenspiel mit dem ebenfalls schlanken Spring Boot ist das relativ dicht an Magie. Zum Glück bleibt ja jedem überlassen, ob er sich verzaubern lassen möchte.
Im nächsten Artikel schauen wir uns an, wie sich Spring-Anwendungen über Properties konfigurieren lassen.
(ID:47299205)