Über unsMediaKontaktImpressum
Florian Schneider 20. Februar 2024

KTOR oder Spring Boot? Vergleich der Web-Frameworks

Spring oder Spring Boot ist in Deutschland, wenn man in die Unternehmen schaut, stark verbreitet und gehört für viele von uns zum täglichen Brot. Immer mehr stelle ich eine ansteigende Tendenz fest, dass Spring Boot nicht mit Java, sondern mit Kotlin Verwendung findet. Kotlin ist eine moderne Programmiersprache, die in den letzten Jahren zu einer immer beliebteren Wahl für Web-Entwicklungsprojekte, Android-Entwicklung, aber auch Backend-Entwicklung wird. Für Backend-Services sind dabei oft Frameworks wie Spring Boot, Quarkus, Micronaut etc. im Einsatz. Mit der Einführung von KTOR im Jahr 2017 hat die Kotlin-Community ein neues Standard-Web-Framework erhalten, das sich durch seine Leistungsfähigkeit, Einfachheit und Flexibilität auszeichnet.

KTOR ist ein HTTP-Server-Framework, das auf Kotlin coroutines basiert. Coroutines ermöglichen es Entwickler:innen, asynchrone Operationen zu schreiben, ohne dass sie sich um die Verwaltung von Threads kümmern müssen. Dies macht KTOR zu einer idealen Wahl für die Entwicklung von hochperformanten und skalierbaren Web-Anwendungen.

Dabei hält sich die Lernkurve flach, da KTOR mit Kotlin leicht zu erlernen ist. Das Framework verfügt über eine intuitive API, die es Entwickler:innen ermöglicht, schnell und einfach Web-Anwendungen zu erstellen. Es unterstützt außerdem eine Vielzahl von HTTP-Funktionen, einschließlich HTTP/2, WebSockets und Server-Sent Events.

Ein paar Vorteile von KTOR:

  • Leistung: KTOR ist dank der Verwendung von Kotlin coroutines ein sehr leistungsstarkes Framework.
  • Einfachheit: KTOR ist einfach zu lernen und zu verwenden.
  • Flexibilität: KTOR unterstützt eine Vielzahl von HTTP-Funktionen und -Protokollen.

Ein paar KTOR-Anwendungsfälle:

  • Web-APIs: KTOR ist ideal für die Entwicklung von RESTful-Web-APIs.
  • Web-Apps: KTOR kann verwendet werden, um komplexe Web-Anwendungen mit einer Vielzahl von Funktionen zu erstellen.
  • Serverless Computing: KTOR kann mit Serverless-Plattformen wie AWS Lambda und Azure Functions verwendet werden.

Erstmal ein Hello World

Generell braucht es für einen KTOR-Service nicht viel Code. Listing 1 zeigt ein einfaches Hello-World-Beispiel. Natürlich weit weg von realen Projekten und Code Bases.

Listing 1: KTOR Hello World

import io.ktor.server.application.* 
import io.ktor.server.routing.* 
import io.ktor.server.response.* 
import io.ktor.server.request.*

fun main(args: Array<String>) {
	embeddedServer(Netty, port = 8080) {
		routing { 
			get("/") {
				call.respondText("Hello, world!")
			}
		}
	}.start(wait = true)
}

Das Programm aus Listing 1 enthält folgende Key-Funktionen:

  • fun main(args: Array<String>) – Die main()-Funktion ist der Einstiegspunkt für die Anwendung.
  • embeddedServer(Netty, port = 8080) – erstellt einen eingebetteten Server mit der Netty-Engine und dem Port 8080.
  • routing {} – Die routing()-Funktion definiert die Routing-Regeln für die Anwendung. Es ist eines der Standard-Plugins (auf die Plugins kommen wir später in diesem Artikel).
    • get("/") {} – definiert eine Route für den Pfad /.
    • call.respondText("Hello, world!") – gibt die Nachricht "Hello, world!" zurück.

Liegt das obere Beispiel jetzt in einem File, bspw. hello-world.kt, kann es nun mittels des Kotlin-Compilers in ein lauffähiges Programm übersetzt werden, ein Standalone-Fat-Jar (Listing 2).

Listing 2: kotlin compiler command

kotlinc hello-world.kt -include-runtime -d hello-world.jar

Das erstellte Fat-Jar kann man nun auf der Java Virtual Machine (JVM) laufen lassen (Listing 3) und im Anschluss kann auf den Service mittels Browser zugegriffen werden (Adresse: http://localhost:8080).

Listing 3: kotlin compiler command

java -jar hello-world.jar

KTOR-Plugins

In Listing 1 tauchte schon eines der Standard-Plugins von KTOR auf: Routing. Plugins werden zur einfachen Erweiterung einer KTOR-Applikation verwendet und können für eine Vielzahl von Aufgaben eingesetzt werden, z. B. für das Routing, das Logging, die Authentifizierung und die Sicherheit, Datenbank-Support usw.

KTOR bietet eine Reihe von Standard-Plugins, die für die meisten Anwendungsfälle geeignet sind. Dazu gehören zum Beispiel:

  • Routing: Das Routing Plugin ermöglicht es Ihnen, Routen für Ihre Anwendung zu definieren.
  • Logging: Das Logging-Plugin ermöglicht es Ihnen, die Aktivitäten Ihrer Anwendung zu protokollieren.
  • Auth: Das Authentifizierung-Plugin ermöglicht es Ihnen, Benutzer zu authentifizieren und zu autorisieren.
  • Datenbank-Support: Plugins für eine Reihe von Datenbanken, darunter PostgreSQL, MySQL und MongoDB.

Im Folgenden schauen wir uns zwei Kern-Plugins einmal genauer an: das Routing- und das Auth-Plugin.

Detailblick in das Kern-Plugin – Routing

Das Routing-Plugin ist ein Kern-Plugin von KTOR. Hiermit werden API/HTTP-Routen definiert. Routen werden mit der routing()-Funktion definiert, welche eine Reihe von Methoden beinhaltet, die verwendet werden können, um verschiedene Arten von Routen zu definieren. Die einfachste Art, eine Route zu definieren, ist die Verwendung der get()-Methode, um z. B. HTTP-GET-Anfragen an einen bestimmten Pfad zu beantworten. In Listing 1 haben wir bereits eine simple Form einer GET-Route kennengelernt. Blicken wir nun auf Beispiele, wie es üblicherweise in einer KTOR- Applikation strukturiert ist.
 
In unserem Application.kt-File (Listing 4) haben wir weiterhin eine main()-Funktion, welche der Startpunkt der Applikation ist. Wie im simplen Hello-World-Beispiel ist auch hier der embeddedServer konfiguriert. In Listing 4 wird ebenso Netty verwendet, ein Port gesetzt, die Host-IP angegeben und zusätzlich ein module referenziert. Im File weiter unten wird dann mittels Application.module() die Konfiguration verschiedener Plugins von der main()-Funktion entkoppelt. Das dient der Lesbarkeit und Übersicht.

Listing 4: Application.kt

fun main() {
	embeddedServer(Netty, port = 8080, host = "0.0.0.0", module = Application::module)
		.start(wait = true)
}

fun Application.module() {
	configureRouting()
}

Die Routing-Konfiguration wird ebenfalls in ein eigenes File ausgelagert (Listing 5). Hier sieht man beispielhaft die Konfiguration einer "Standard-Route" (/customer), unter welcher bestimmte HTTP-Operationen verwendet werden – hier GET, PUT und DELETE. An dieser Stelle würde z. B. der Code für die Abfrage von Kundendaten (GET), die Erstellung eines Kunden (PUT) oder das Löschen eines Kunden geschrieben werden. In Listing 5 ist hier auch beispielsweise gezeigt, wie man auf Pfad-Parameter zugreifen kann (get(“/id”) { … }).

Listing 5: Customer-Routes.kt

fun Application.configureRouting() {
	routing {
		route("/customer") {
			get {
				...
			}
			
			get("/{id}") {
				val id = call.parameters["id"]
				...
			}

			put {
				...
			}
			
			
			delete("/{id}") {
				val id = call.parameters["id"]
				...
			}
		}
	}
}

In Listing 5 sehen wir ein weiteres Feature des Routing-Plugins: Route-Gruppen. Unterhalb der Routing-Funktion können mehrere Route-Gruppen existieren. Im Beispiel-Code aus Listing 5 wäre eine solche Gruppe route(“/customer”). Gruppen können hierbei verschachtelt werden. Darüber hinaus gibt es weitere Unterstützung für Wildcard-Routen und Regex-Routen. Die KTOR-Dokumentation ist an dieser Stelle zu erwähnen [1].

Detailblick in das Kern-Plugin – Auth

Das Auth-Plugin von KTOR ermöglicht es Entwickler:innen, Authentifizierung und Autorisierung für ihre Anwendungen zu implementieren. Das Plugin unterstützt eine Reihe von HTTP-Authentifizierungsmechanismen, darunter:

  • Basic Auth: ein einfacher Authentifizierungsmechanismus, der einen Benutzernamen und ein Passwort verwendet.
  • Digest Auth: ist eine sicherere Alternative zu Basic Auth, die die Benutzeranmeldung verschlüsselt.
  • Bearer Auth: ist ein Authentifizierungsmechanismus, der einen Token verwendet, um den Zugriff auf geschützte Ressourcen zu autorisieren.

Darüber hinaus gibt es die Form-based-Authentication, um User-Credentials über ein Web-Formular einzusammeln. Auth-Typen wie LDAP, OAuth und JWT werden auch über das Plugin unterstützt. Wenn man einen anderen Auth-Typ braucht, bietet KTOR eine API für die Erstellung von Custom-Plugins. Damit können auch eigene Plugins zur Authentication programmiert werden (mehr zu Custom-Plugins später im Artikel). Um das Auth-Plugin zu verwenden, müssen Entwickler zunächst das Plugin in ihrer Anwendung installieren. Dies kann mit der install()-Methode des Application-Objekts erfolgen (Listing 6).

Listing 6: Install-Auth-Plugin

fun main() {
	embeddedServer (Netty, port = 8080) {
		install(Authentication)
		// ...
	}.start(wait = true)
}

In Listing 7 sehen wir eine beispielhafte Konfiguration einer simplen Basic Auth. Diese Konfiguration kann schließlich im Router über den Namen (hier: auth-basic) verwendet werden (Listing 8). Weitere Informationen zu Standard-Plugins und deren Verwendung findet man in der Kotlin-Dokumentation [1].

Listing 7: Basic-auth-Konfiguration

install(Authentication) {
	basic("auth-basic") {
		realm = "Access to the '/' path"
		validate { credentials ->
			if (credentials.name == "foo" && credentials.password == "bar") {
				UserIdPrincipal(credentials.name)
			} else {
				null
			}
		}
	}
}

Listing 8: Basic-auth-Konfiguration

fun Application.configureRouting() {
	routing {
		route("/customer") {
			get { ... }
			
			authenticat ("auth-basic") {
				get("/{id}") { ... }

				...
			}
		}
	}
}

Custom-Plugins

Mit Custom-Plugins können Entwickler:innen KTOR um zusätzliche Funktionalität erweitern. Custom-Plugins können zum Beispiel verwendet werden, um:

  • neue Routen zu definieren,
  • neue Logging- oder Authentifizierungsmechanismen zu implementieren und
  • Support für neue Datenbanken hinzuzufügen.

Der einfachste Weg, um ein Custom-Plugin zu erstellen, ist es, die Application-Methode createApplicationPlugin() zu verwenden. Diese Methode gibt eine Instanz von ApplicationPlugin zurück. Es gibt zusätzlich noch die Methode createRouteScopedPlugin(), welche an einer bestimmten Route im Routing-Plugin installiert werden kann. In Listing 9 sehen wir ein einfaches Custom-Plugin, welches lediglich einen Consolen-Output erzeugt. Dieses Plugin wird wie die Standard-Plugins über die Application install()-Methode in die KTOR-Applikation eingehängt (Listing 10). Der Output wird generiert, sobald das Plugin installiert wird (Listing 11).

Listing 9: Simples Custom-Plugin

val MySimpleCustomPlugin = createApplicationPlugin("mySimpleCustomPlugin") {
	println("plugin installed ... yay")
}

Listing 10: Installation des Custom-Plugins

fun Application.module() {
	install(MySimpleCustomPlugin)

	...
}

Listing 11: Output bei Applikationsstart

2024-01-31 13:33:01.610 [main] INFO ktor.application - Autoreload is disabled because ...
2024-01-31 13:33:02.171 [main] INFO [Koin] - Koin started with 2 definitions in 0.473041 ms
plugin installed... yay
2024-01-31 13:33:02.216 [main] INFO ktor.application - Application started in 0.664 seconds.

Spring Boot vs. KTOR

Spring Boot und KTOR sind beides Web-Frameworks, die für die Entwicklung von Web-Anwendungen verwendet werden können und bieten eine Reihe von Funktionen, die die Entwicklung von Web-Anwendungen vereinfachen und beschleunigen können. Der wichtigste Unterschied zwischen Spring Boot und KTOR ist die Sprache, auf der sie basieren. Spring Boot basiert auf Java, während KTOR auf Kotlin basiert. Kotlin ist eine moderne Programmiersprache, die sich durch ihre Einfachheit, Klarheit und Produktivität auszeichnet.

Wenn wir die Leistung beider Frameworks unter die Lupe nehmen, ist KTOR aufgrund der Verwendung von Kotlin-Coroutines ein sehr leistungsstarkes Framework. Wie eingangs in diesem Artikel beschrieben, ermöglichen Coroutines den Entwickler:innen, asynchrone Operationen zu schreiben, ohne dass sie sich um die Verwaltung von Threads kümmern müssen – ideal für die Entwicklung von hochperformanten und skalierbaren Webanwendungen.

Spring Boot hat eine etwas steilere Lernkurve als KTOR, welches am Umfang von Spring Boot liegt. Spring Boot bietet – Stand heute – mehr Funktionen und Optionen. KTOR ist hingegen ein eher minimalistisches Framework, das sich dadurch leichter erlernen lässt. Spring Boot hat darüber hinaus noch eine größere Community und ein umfangreicheres Ecosystem als KTOR, was natürlich auch am Alter der Frameworks liegt. Dies bedeutet, dass es für Spring Boot mehr Ressourcen und Unterstützung gibt, z. B. Tutorials, Dokumentationen und Bibliotheken. Allerdings ist zu beobachten, dass die Community um KTOR stetig wächst und das Framework an Beliebtheit gewinnt. Nicht zu verachten ist auch die Tatsache, dass sich hinter KTOR (ebenso wie hinter Kotlin) das Unternehmen JetBrains befindet, welches ein eigenes Interesse an der Weiterentwicklung hat.

Wenn man auf Ebene der Anwendungsfälle einen Vergleich zieht, lässt sich sagen, dass Spring Boot ein vielseitiges Framework ist und für eine Vielzahl von Anwendungsfällen genutzt werden kann. KTOR ist hingegen eher auf die Entwicklung von RESTful-Web-APIs und Microservices spezialisiert.

Fazit

Sowohl Spring Boot als auch KTOR sind leistungsstarke Web-Frameworks, die für die Entwicklung von Web-Anwendungen eingesetzt werden können. Die Wahl des richtigen Frameworks hängt von den individuellen Anforderungen und Präferenzen der Entwickler:innen bzw. des Projekt- und/oder Team-Kontextes ab.

KTOR ist eine gute Wahl für Euch, wenn Ihr

  • eine moderne Programmiersprache bevorzugt,
  • Wert auf Leistung legt,
  • ein minimalistisches Framework sucht oder
  • schnell und einfach loslegen möchtet.

Spring Boot ist eine gute Wahl für Euch, wenn Ihr

  • ein umfangreiches Framework mit vielen Funktionen bevorzugt,
  • Wert auf Community und Support legt oder
  • eine breite Palette von Anwendungsfällen abdecken möchtet.
Autor
Florian Schneider

Florian Schneider

Florian ist seit 2018 für codecentric am Standort Frankfurt tätig. Sein Schwerpunkt liegt im Java-Backend-Umfeld und agiler Software-Entwicklung. Er ist ein großer Fan von Clean Code und Refactoring.
>> Weiterlesen
Das könnte Sie auch interessieren
Kommentare (0)

Neuen Kommentar schreiben