Ich möchte mit meiner persönlichen Reise in der Softwareentwicklung beginnen, um meine Perspektive für den Vergleich zu setzen. Am Anfang meiner beruflichen Laufbahn war ich in einem großen Projekt, das alles andere als modern war. Die Anwendung war ein typischer „Big Ball of Mud“: eine Mischung aus Servlet, JSP, JSF1 und JSF2. Irgendwann haben wir uns entschieden, eine neue einheitliche Architektur mit dem damals modernen Java EE 5 bzw. 6 zu bauen. Das war meine erste Erfahrung mit Java EE. In dieser Zeit habe ich viel über die grundlegenden Konzepte gelernt, wie man Java EE sinnvoll einsetzt.
Nach diesem Projekt arbeitete ich in einem Unternehmen, das Java-EE-Systeme beim Kunden einsetzte. Das System lief meistens on Premise beim Kunden. Wir hatten eine einzige Codebasis, die auf über hundert Kundensystemen lief – mit jeweils unterschiedlichen Konfigurationen, insbesondere mit verschiedenen Kombinationen aus Application-Server und Datenbank. Das war mein erster Kontakt mit der Idee von Kompatibilität in Java EE. Ich habe erlebt, wie robust diese Technologie sein kann, selbst wenn sie auf verschiedenen Application-Servern wie WebSphere, JBoss oder NetWeaver eingesetzt wird.
Stay tuned
Regelmäßig News zur Konferenz und der Java-Community erhalten
Ab 2018 wechselte ich zu Spring Boot. Und was soll ich sagen: Es war ein Gamechanger. Viele Aufgaben, die früher mühsam waren, wurden durch Spring Boot praktisch und komfortabel automatisiert. Ein besonderes Highlight waren die Datenbank-Repositories mit der automatischen Generierung der JPQL-Querys aus dem Methodennamen. Das fand ich genial.
Als ich dann im Jahr 2023 ein Quarkus-Projekt angeboten bekam, wollte ich es eben wegen Quarkus haben. Meine Erwartung war, dass sich die Vorzüge von Spring Boot und Jakarta EE (ehemals Java EE) vereinen: moderne, komfortable Entwicklung zusammen mit offiziellen Standards, die man nur einmal lernen muss und die einfacher zu migrieren sind. Ich wollte verstehen, wie sich dieses neue Framework im Vergleich zu Spring Boot schlägt. Nach den ersten Monaten mit Quarkus war die Idee zu einem Vortrag geboren, der die Grundlage für diesen Artikel bildet.
Spring Boot und Quarkus
Spring Boot ist ein Open-Source-Framework, das von VMware entwickelt wurde. Es wurde 2013 veröffentlicht und basiert auf dem Spring-Framework, das seit 2003 existiert. Spring Boot ermöglicht es Entwicklern, eigenständige, produktionsreife Anwendungen zu erstellen, die ohne großen Aufwand direkt einsatzbereit sind. Auf GitHub wird es mit 75,1 k Stars, 40,7 k Forks und 3,4 k Beobachtern (Stand: November 2024) verzeichnet, und es gibt über 528 000 Ergebnisse für Spring-Boot-Projekte.
Quarkus wiederum ist ein Open-Source-Framework von Red Hat, das 2019 veröffentlicht wurde. Es ist speziell für Java-Anwendungen optimiert, die in Kubernetes-Umgebungen laufen. Quarkus basiert auf Eclipse MicroProfile und nutzt Technologien wie OpenJDK HotSpot und GraalVM. Auf GitHub hat Quarkus 13,8 k Stars, 2,7 k Forks und 259 Beobachter. Es gibt 24,6 k Quarkus-bezogene Repositories (Stand: November 2024).
Mein Vergleich: Entwicklererfahrungen im Fokus
Im Folgenden will ich nicht die technischen und operativen Vorteile von Quarkus vergleichen. Vergleiche der Startzeiten und des Speicherverbrauchs können an anderer Stelle nachgelesen werden. Hier fokussiere ich mich bewusst auf die Developer Experience.
Der Vergleich zwischen Spring Boot und Quarkus basiert auf meiner praktischen Arbeit mit beiden Frameworks. Als roten Faden habe ich eine Spring-Boot-Anwendung möglichst ähnlich mit Quarkus nachgebildet. Für die Vergleichbarkeit habe ich folgende Rahmenbedingungen gesetzt:
- Datenbankschema und Frontend unverändert: Die JPA-Entities und das REST-basierte Frontend sollten möglichst eins zu eins austauschbar sein.
- Im eigenen Ökosystem bleiben: Es gibt Möglichkeiten, Teile eines Frameworks in anderen zu nutzen, z. B. JAX-RS in Spring Boot oder Spring Data in Quarkus. Für den Vergleich der Developer Experience habe ich das bewusst ausgeschlossen.
- Keine Out-of-the-box-Endpoints: Automatisch generierte Endpoints, wie sie etwa Spring Data REST bietet, wurden nicht berücksichtigt, da hier oft die Möglichkeit fehlt, individuelle fachliche Logik einzubauen.
Für den Vergleich habe ich folgende Aspekte untersucht:
- Dokumentation
- Start
- REST-Interface
- Dependency Injection
- ORM
- Authentication
- Developer-Tools
- Docker Builds
- Testing
- Native
Bei der Spring-Boot-Anwendung handelt es sich um eine einfache Blogsoftware, die ein REST-Interface hat, zudem Spring Boot 3.3, Spring Security mit Basic Auth, Daten in einer PostgreSQL-Datenbank speichert und über ein einfaches Frontend mit Vanilla JS, Fetch API und Bootstrap verfügt. Diese Software verwende ich üblicherweise als Lehrbeispiel, da man hier alle Aspekte einer modernen Webanwendung findet und sie dennoch einfach genug ist. Die originale Spring-Boot-Anwendung ist unter [1] zu finden, die migrierte Quarkus-Anwendung unter [2].
Dokumentation
Die Dokumentation von Spring Boot [3] ist umfangreich und gut strukturiert. Es gibt offizielle Guides, die von der Installation bis zu fortgeschrittenen Themen reichen.
Quarkus setzt auf einen pragmatischen Ansatz [4]. Die Guides sind oft kürzer, dafür aber praxisorientiert. Besonders hervorzuheben ist die spezifische Dokumentation zur nativen Image-Erstellung und Kubernetes-Integration.
Hier will ich noch anmerken, dass Red Hat das Buch „Quarkus for Spring Developers“ von Eric Deandrea [5] kostenfrei zur Verfügung stellt, was eine gute Quelle für Umlernende ist. Zudem stimme ich Adam Bien zu, wenn er auf die Kritik an der Quarkus-Doku eingeht, dass die Quarkus-Guides zu komplex seien. Da Quarkus auf dem MicroProfile des Jakarta-EE-Standards basiert [6], kann man diese ebenfalls gut nutzen und muss sie nicht extra bei Quarkus aufführen; dazu ist das Video „How To Learn Quarkus“ von Adam Bien empfehlenswert [7].
Mein Fazit zur Dokumentation: Beide Frameworks bieten eine solide Dokumentation. Hier gibt es keinen klaren Gewinner.
Start
Sowohl Spring Boot mit dem Spring Initializr [8] als auch Quarkus [9] bieten Websites an, um ein Projekt-Set-up einfach zu generieren (Abb. 1 und 2). Sie existieren jeweils auch als CLI-Werkzeug und sind auch in den gängigsten IDEs integriert. Mein Fazit zum Start: Beide Frameworks bieten hier die gleiche Funktionalität an.
Abb. 1: Projekt-Set-up mit Spring Initializr …
Abb. 2: … und mit Quarkus
REST-Interface
Spring Boot macht die Arbeit mit REST-Interfaces besonders einfach. Mit Annotationen wie @RestController und @GetMapping fügt es sich perfekt in das Spring-MVC-Framework ein. Besonders praktisch: Funktionen wie die Paginierung sind schon eingebaut und können ohne großen Aufwand genutzt werden. Hier das Beispiel eines GET Endpoint mit Paginierung in Spring Boot:
@GetMapping(path = "/entries", produces = MediaType.APPLICATION_JSON_VALUE) public Page<Entry> getAllEntries(@ParameterObject Pageable pageable) { return entryService.getAllEntries(pageable); }
Quarkus hingegen setzt auf den JAX-RS-Standard und verwendet Annotationen wie @Path und @GET. Es integriert RESTEasy sowie andere JAX-RS-Implementierungen und bietet damit eine saubere und standardisierte Lösung. Allerdings fehlt eine direkte Unterstützung der Paginierung innerhalb des ORM, was zusätzlichen Entwicklungsaufwand erfordern kann. Die Nachimplementierung dieser Funktionalität empfand ich als besonders umständlich. Das Beispiel eines GET Endpoints mit manueller Paginierung in Quarkus zeigt Listing 1.
Listing 1
@GET @Transactional public Response getAllEntries( @QueryParam("sort") List<String> sortQuery, @QueryParam("page") @DefaultValue("0") int pageIndex, @QueryParam("size") @DefaultValue("10") int pageSize) { // Idea from https://quarkus.io/guides/rest-data-panache#hr-generating-resources // But I did not find the getSortFromQuery, so I implemented it my self Page page = Page.of(pageIndex, pageSize); Sort sort = getSortFromQuery(sortQuery); List<Entry> entires = entryService.getAllEntries(sort, page).list(); Long allEntriesCount = entryService.getAllEntriesCount(); PageOutput<Entry> pageOutput = PageOutput.of(entires, pageIndex, pageSize, allEntriesCount); return Response.ok(pageOutput).build(); }
Mein Fazit zum REST-Interface: Spring Boot punktet hier durch die sofort einsatzbereite Paginierung und die saubere Möglichkeit zur Änderung des Standard-HTTP-Codes mittels der @ResponseStatus-Annotation gegenüber Quarkus.
Dependency Injection
Spring Boot nutzt die für das Spring-Framework typischen Annotationen wie @Autowired, @Component und @Service für die Dependency Injection. Eine Schwäche ist das sogenannte Self-Inject-Problem: Wenn sich eine Bean selbst injiziert, können Probleme auftreten, da man das proxy-Objekt nicht aufruft und so z. B. keine neue Transaktion auslöst, obwohl es so annotiert ist. Die Definition eines Service in Spring Boot mit Konstruktorinjektion zeigt Listing 2.
Listing 2
@Service public class EntryService { private final EntryRepository entryRepository; private final TagRepository tagRepository; public EntryService(EntryRepository entryRepository, TagRepository tagRepository) { this.entryRepository = entryRepository; this.tagRepository = tagRepository; } [...] }
Quarkus setzt auf den CDI-Standard. Annotationen wie @Inject, @ApplicationScoped und @Singleton sorgen für eine saubere Integration. Auch hier tritt das Self-Inject-Problem auf, was zeigt, dass es kein spezifisches Problem von Spring Boot ist. Die Definition eines Service in Quarkus mit Konstruktorinjektion zeigt Listing 3.
Listing 3
@ApplicationScoped public class EntryService { private final EntryRepository entryRepository; private final TagRepository tagRepository; public EntryService(EntryRepository entryRepository, TagRepository tagRepository) { this.entryRepository = entryRepository; this.tagRepository = tagRepository; } [...] }
Mein Fazit zur Dependency Injection: Beide Frameworks sind in diesem Bereich gleichwertig.
ORM (Object-relational Mapping)
Spring Boot verwendet Spring Data JPA. Die Möglichkeit, Methoden einfach durch interface-Definitionen zu implementieren, ist ein großer Vorteil. Zudem reicht ein JDBC-Treiber, um eine Datenbank anzubinden. Ein Spring Data Repository mit interface-Definition sieht so aus:
public interface EntryRepository extends JpaRepository<Entry, Long> { List<Entry> findByTags_NameOrderByCreatedDesc(String tagName); List<Entry> findByAuthor(BlogUser author, Pageable pageable); }
Quarkus bietet Panache als Ergänzung zu JPA. Diese Lösung ist zwar elegant, erreicht aber nicht den Komfort von Spring Data JPA. Jakarta Data könnte hier irgendwann einmal die gleiche Funktionalität liefern, aber die Bibliothek ist noch nicht so weit, den vollen Umfang abzubilden, den Spring Data bietet. Besonders auffällig ist, dass Quarkus bei der Standardgenerierung von Datenbankschemas Unterschiede aufweist, was bei Migrationen zu Problemen führen kann. Hier musste ich in der application.properties den Wert quarkus.hibernate-orm.physical-naming-strategy=org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy setzen, um das gleiche Verhalten zu erhalten. Außerdem scheinen die Möglichkeiten, Datenbanken einzubinden, eingeschränkt, da hier ein einfacher JDBC-Treiber nicht ausreicht, sondern eine JDBC Driver Extension dafür existieren muss, siehe [10]. Für die gängigsten Datenbanken findet man diese zwar, aber nicht für alle. Ein Beispiel für Quarkus mit JPQL und PanacheRepository zeigt Listing 4.
Listing 4
@ApplicationScoped public class EntryRepository implements PanacheRepository<Entry> { public List<Entry> findByTags_NameOrderByCreatedDesc(String tagName) { return list("SELECT e FROM Entry e JOIN e.tags t WHERE t.name = ?1 ORDER BY e.created DESC", tagName); } public List<Entry> findByAuthor(BlogUser author, Sort sort, Page page) { return find("author", sort, author).page(page).list(); } }
Mein Fazit zum ORM: Spring Boot ist hier für mich deutlich überlegen.
Developer-Tools
Spring Boot bietet DevTools für Hot Reload und Actuator für Monitoring. Das erleichtert die Entwicklung und Überwachung von Anwendungen.
Der Dev Mode von Quarkus ist ein echtes Highlight. Änderungen im Code werden (meistens) sofort übernommen, ohne dass die Anwendung neu gestartet werden muss. Die Dev UI bietet eine übersichtliche Darstellung von Beans, Endpunkten und Konfigurationen.
Mein Fazit zu Developer-Tools: Quarkus bietet eine modernere Entwicklererfahrung.
Docker Builds
Spring Boot bietet praktische Möglichkeiten, Docker Images zu erstellen. Mit dem Spring-Boot-Maven-Plug-in können containerisierte Anwendungen direkt gebaut werden. Spring Boot verwendet Buildpacks, um Images ohne die Erstellung eines Dockerfiles zu generieren. Die Imagegröße liegt hier bei etwa 385 MB.
Quarkus hingegen liefert ein Dockerfile direkt mit, was Entwicklern die Konfiguration erleichtert. Darüber hinaus gibt es das Plug-in quarkus.container-image, das den Build-Prozess ebenfalls vereinfacht. Allerdings ist die standardmäßige Imagegröße mit 502 MB etwas größer als bei Spring Boot.
Mein Fazit zu Docker Builds: Spring Boot hat in diesem Bereich einen Vorteil, da es kleinere Images erzeugt und man sich durch Buildpacks die Wartung der Dockerfiles spart.
Stay tuned
Regelmäßig News zur Konferenz und der Java-Community erhalten
Native Builds
Spring Boot unterstützt die Erstellung nativer Images durch das Plug-in org.graalvm.buildtools:native-maven-plugin. Um ein natives Image zu erstellen, wird der Befehl mvn -Pnative spring-boot:build-image verwendet. Das resultierende Image hat eine Größe von etwa 225 MB.
Quarkus bietet ebenfalls eine einfache Möglichkeit, native Images zu erstellen. Mit dem Befehl ./mvnw package -Dnative wird das native Image erzeugt. Zusätzlich kann mit docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/simple-blog-quarkus-fetch ein Docker Image erstellt werden. Es ist mit etwa 133 MB deutlich schlanker. Quarkus liefert außerdem umfassende Dokumentationen zur Kompatibilität und zu unterstützten Bibliotheken, die auf der GraalVM-Website verfügbar sind.
Mein Fazit zu Native Builds: Quarkus wurde mit Blick auf Native Builds entwickelt. Spring Boot hat in diesem Bereich seit Version 3 deutlich aufgeholt, aber Quarkus bleibt führend. Da Native Builds deutlich länger kompilieren, sollten sie in der täglichen Entwicklung lokal keine Rolle spielen. Man kann sie in Pipelines auslagern. Allerdings muss man bei Native Builds aufpassen: Nicht jede Java-Bibliotheken ist auch kompatibel mit nativen Images [11].
Testing
Spring Boot bietet mit @SpringBootTest eine Annotation, die den gesamten Spring-Kontext für Tests lädt. Damit lassen sich umfassende End-to-End-Tests durchführen, allerdings kann das Laden des gesamten Kontexts zu längeren Testzeiten führen.
Quarkus hat hier einige interessante Ansätze. Mit @QuarkusTest wird der gesamte Quarkus-Kontext geladen, ähnlich wie bei Spring Boot. Zusätzlich gibt es @QuarkusIntegrationTest, das eine Quarkus-Instanz in einer separaten JVM startet, was für Integrationstests nützlich ist. Ein besonderes Highlight von Quarkus ist die automatische Unterstützung von Testcontainern. Ohne zusätzliche Konfiguration werden beispielsweise Datenbankcontainer automatisch gestartet, was den Testaufbau erheblich vereinfacht.
Mein Fazit zum Testing: Quarkus bietet in diesem Bereich durch die automatische Testcontainer-Integration und flexible Testmöglichkeiten mehr Komfort als Spring Boot.
Fazit: Es kommt darauf an
Mein abschließendes Fazit lautet, dass die Frameworks sehr ähnlich sind, sodass sich ohne fachlichen Use Case eine Migration in die eine oder andere Richtung nicht lohnt.
Welche soll man nun wählen? Die beste Technologie ist die, die euer Team beherrscht. Der Erfolg eines Projekts hängt auch hier nicht hauptsächlich von der Wahl des Frameworks ab, sondern von den Menschen, die damit arbeiten. Wenn das Team in Spring Boot fit ist, dann hat es ohne fachliche Anforderungen keinen Sinn, Quarkus einzusetzen. Es sei denn, das Team ist neugierig und möchte Quarkus lernen. Wenn das Team die Jakarta-EE-Standards im Schlaf beherrscht und mit Spring nicht zurechtkommt, gibt es keinen Grund Spring Boot einsetzen. Also: Hört auf eure Teams.
Links & Literatur
[1] https://github.com/gruemme/simple-blog-sb-fetch
[2] https://github.com/gruemme/simple-blog-quarkus-fetch
[3] https://spring.io/projects/spring-boot
[4] https://quarkus.io/guides/
[5] Deandrea, Eric: „Quarkus for Spring Developers“: https://developers.redhat.com/e-books/quarkus-spring-developers
[7] https://www.youtube.com/watch?v=H7O7mIJCLFY
[10] https://quarkus.io/guides/hibernate-orm#setting-up-and-configuring-hibernate-orm
[11] https://www.graalvm.org/latest/reference-manual/native-image/metadata/Compatibility