Wir haben es uns zur Aufgabe gemacht, verschiedene Microframeworks anhand eines kleinen Anwendungsfalles zu evaluieren, der detaillierte Anwendungsfall wurde bereits in unserem ersten Blog-Post beschrieben. Unser Artikel über Javalin ist unter dieser Adresse zu finden. Heute werden wir uns auf das Microframework Micronaut konzentrieren. Im Folgenden zeigen wir die wichtigsten Eigenschaften, Stärken und Schwächen von Micronaut. Das Codebespiel ist unter diesen Link auf Github zu finden.
Bei Micronaut handelt es sich um ein modernes, JVM-basiertes Full-Stack-Framework zum Aufbau modularer, leicht testbarer Microservices- und Serverless Anwendungen. Das Framework unterstützt die Implementierung in Java, Groovy und Kotlin. Weitere Eigenschaften des Frameworks sind ein geringer Speicherbedarf und eine schnelle Start-Up Zeit, was vor allem in Bezug auf die Verwendung im Serverless-Umfeld wichtig ist. Außerdem wird Micronaut unter Apache License 2.0 veröffentlicht.
Wenn man mit Micronaut arbeitet, ist häufig die Implementierung/Erweiterung bestehender Framework-Klassen notwendig. Einige Aspekte können einfach durch die Verwendung der entsprechenden Annotationen (@GET, @POST“¦) deklarativ hinzugefügt werden.
In diesem Teil haben wir erstmal allgemeine Informationen über Micronaut gegeben. Im Folgenden betrachten wir wie eine Micronaut Applikation entwickelt wird und beschreiben wichtigste Eigenschaften und Elemente des Frameworks.
Micronaut Projekt erstellen und kompilieren
Bevor wir anfangen, stellen wir sicher, dass wir ein Java 8 SDK oder höher installiert haben
Micronaut bietet eine CLI an. Mit Hilfe der CLI kann man Projekte erstellen und löschen. Verschiedene Möglichkeiten die Micronaut CLI zu installieren finden sich in der offiziellen Dokumentation (https://docs.micronaut.io/latest/guide/index.html#buildCLI).
Um Micronaut CLI benutzen zu können, muss man zuerst die letzte Binärdatei aus https://micronaut.io/download.html herunterladen und die an einen geeigneten Ort extrahieren. Danach wird eine Umgebungsvariable MICRONAUT_HOME erstellt, die auf das Installationsverzeichnis zeigt und die Umgebungsvariable PATH wird aktualisiert. Nachher kann man das Micronaut CLI ausführen über die Command Line ausgeführt werden (siehe Abbildung 1).
Über das Micronaut CLI kann einen neue Micronaut Anwendung am schnellsten und einfachsten erzeugt werden.
1. $ mn
2. | Starting interactive mode...
3. | Enter a command name to run. Use TAB for completion:
4. mn>
Abbildung 1-Micronaut CLI von der Eingabeaufforderung starten
Der folgende Befehl (siehe Abbildung 2) erstellt eine neue „micronaut-demo“- Anwendung in Java mit einem Gradle-Build.
1. mn create-app micronaut-demo
Abbildung 2-Applikation über Micronaut CLI erstellen
Wenn Maven anstelle von Gradle verwendet werden soll, kann dies im Rahmen der Anwendungserstellung mittels des Parameters „–build maven“ definiert werden. Eine Gradle-baiserte Anwendung kann mit dem folgenden Befehl ausgeführt werden:
1. $ ./gradlew run
2. > Task :run
Abbildung 3 - Applikation über CLI ausführen
Ein Java-Microservice mit Micronaut
Kommen wir nun zur Implementierung unsereres Microservices. Wir benötigen für unsere Technologie Management Webseite jeweils eine Detailseite pro Technologie. Dafür haben wir erstmal unsere „TechnologyApplication“ Main-Klasse (siehe Abbildung 4) erstellt, diese wird beim Ausführen der Anwendung verwendet.
1. public class TechnologyApplication {
2. public static void main(String[] args) {
3. Micronaut.run(TechnologyApplication.class);
4. }
5. }
Abbildung 4- Main Klasse Technology Applikation
Danach haben wir unsere Controller Klasse implementiert. In dieser Klasse sehen wir schon manche Eigenschaften von Micronaut, und zwar Annotationen(siehe bitte Codebeispiel unter den „LINK“) . GET, POST, PUT, DELETE Methoden werden in der Controllerklasse (siehe bitte Abbildung 5)implementiert und für die Bearbeitung von Requests verwendet. In diesen Methoden neben den Annotationen steht in Klammern („/technologies/{id}“), das ist unsere URI und id ist eine Variable. Beispielsweise werden alle Informationen zu der Technologie Micronaut mit id Nummer 2 unter folgender URL „http://localhost:7000/technologies/2“ zurückgegeben.
Wichtig: Standardmäßig ist der Micronaut Server so konfiguriert, dass er auf Port 8080 läuft. Dies kann aber geändert werden. Wir haben es auch als 7000 geändert. (siehe bitte https://docs.micronaut.io/latest/guide/index.html#runningSpecificPort).
1. @Controller("/")
2. public class TechnologyController {
3. protected final TechnologyRepository technologyRepository;
4.
5. public TechnologyController(TechnologyRepository technologyRepository) {
6. this.technologyRepository = technologyRepository;
7. }
8.
9. @Get("/technologies/{id}")
10. public Technology getTechnology(@PathVariable int id) {
11. return technologyRepository.getTechnology(id).orElse(null);
12. }
13.
14. @Post("/technologies")
15. public HttpResponse insertTechnology(@Body @Valid TechnologyInsertCommand cmd) {
16. "¦"¦"¦"¦"¦"¦"¦"¦
17. }
18.
19. @Put("/technologies/{id}")
20. public HttpResponse updateTechnology(@Body @Valid TechnologyUpdateCommand cmd){
21. "¦"¦"¦"¦"¦"¦"¦
22. }
23.
24. @Delete("/technologies/{id}")
25. public void removeTechnology(@PathVariable int id) {
26. technologyRepository.removeTechnology(id);
27. return location(technology.getId());
28. }
29. }
Abbildung 5 -Controller Klasse
Datenbankverbindung
Wir wollen in unserem Beispiel die Technologien in einer Datenbank speichern oder auch gespeicherte Daten auslesen. Micronaut ermöglicht unter anderem die Anbindung von relationalen Datenbanken via JDBC/JPA/Hibernate. Um uns mit der Datenbank zu verbinden, haben wir uns für MyBatis entschieden. MyBatis ist ein Java-Persistenz-Framework, das Objekte mit Stored Procedures oder SQL-Anweisungen unter Verwendung eines XML-Deskriptors oder von Annotationen koppelt. Im Gegensatz zu ORM-Frameworks bildet MyBatis keine Java-Objekte auf Datenbanktabellen ab (kein Mapping), sondern Java-Methoden auf SQL-Anweisungen.
Um MyBatis benutzen zu können, müssen wir die notwendigen Dependencies in unserer „build.gradle“-Datei hinzufügen.(siehe Abbildung 6)
1. implementation("org.mybatis:mybatis:3.4.6")
2. //MyBatis-Abhängigkeit hinzufügen.
3.
4. implementation("io.micronaut.sql:micronaut-jdbc-hikari")
5. // Konfiguriert SQL-DataSource-Instanzen
6.
7. compile group: 'org.postgresql', name: 'postgresql', version: '42.2.8.jre7'
8. //postgresql jdbc driver dependency
Abbildung 6 "“ build.gradle
Zudem müssen wir unsere Datenquelle in der zentralen Applikationskonfigurationsdatei „application.yml“ Datei definieren (siehe Abbildung 7).
1.datasources:
2. default:
3. url: ${JDBC_URL:`jdbc:postgresql://localhost:5432/postgres`}
4. driverClassName: org.postgresql.Driver
5. username: ${JDBC_USER:postgres}
6. password: ${JDBC_PASSWORD:*********}
Abbildung 7-application.yaml
Da es in Micronaut noch keinen Out-of-the-Box-Support für MyBatis gibt, ist es notwendig, SqlSessionFactory manuell zu verdrahten (siehe Abbildung 8).
1.@Factory
2.public class MyBatisFactory {
3. private DataSource dataSource ;
4. public MyBatisFactory(DataSource dataSource)
{this.dataSource=dataSource;}}
5.
6.@Bean
7.SqlSessionFactory sqlSessionFactory() {
8. TransactionFactory transactionFactory = new JdbcTransactionFactory();
9. Environment environment = new Environment("dev", transactionFactory, dataSource);
10. Configuration configuration = new Configuration(environment);
11. configuration.addMappers("micronaut");
12. return new SqlSessionFactoryBuilder().build(configuration);
13. }
14.}
Abbildung 8-src/main/java/micronaut/MybatisFactory.java
Nachdem Domänenobjekte (in unserem Fall die Klasse „Technology“) erstellt wurden, erstellen wir ein Interface, um die Operationen für den Zugriff auf die Datenbank zu definieren. Wir verwenden die Annotations von MyBatis, um die Methoden auf SQL-Abfragen abzubilden (siehe Abbildung 9).
1. public interface TechnologyMapper {
2.
3. @Select("SELECT * FROM technologies")
4. List getTechnologies();
5.
6. @Select("SELECT * FROM technologies WHERE id=#{id}")
7. Technology getTechnology(int id) ;
8.
9. @Insert("INSERT INTO technologies (name, description, recommendation, relevance, complexity, url,) values(#{name}," +
10. "#{description},#{recommendation},#{relevance},#{complexity},#{url})")
11. void insertTechnology(Technology technology) ;
12.
13. @Delete("DELETE FROM technologies WHERE id=#{id}")
14. void removeTechnology(int id);
15.
16. @Update("UPDATE technologies SET name=#{name}, description=#{description},recommendation=#{recommendation}, " +
17. "relevance=#{relevance}, complexity=#{complexity}, url=#{url}, WHERE id=#{id}")
18. void update(@Param("id") Integer id, @Param("name") String name, @Param("description") String description,
19. @Param("relevance") Integer relevance, @Param("recommendation")Integer recommendation,
20. @Param("complexity")Integer complexity, @Param("url") String url);
21. }
Abbildung 9-TechnologyMapper.java
Nun gibt es noch eine wichtige Sache, und zwar die Erstellung des Datenbankschemas. Dafür haben wir die Micronaut-Integration mit Flyway verwendet. Zunächst wird die Dependency hinzugefügt (Abbildung 10):
1.implementation("io.micronaut.flyway:micronaut-flyway")
Abbildung 10-build.gradle
Danach konfigurieren wir die application.yaml für das Datenbankmigrationsverzeichnis(Abbildung 11):
1. flyway:
2. datasources:
3. default:
4. locations: classpath:db/migration
Abbildung 11 - application.yaml
Nun erstellen wir das Datenbankschema unter dem Verzeichnis „src/main/resources/db/migration/V1__schema.sql“ siehe Abbildung 12:
1. CREATE TABLE technologies(
2. id SERIAL primary key,
3. name VARCHAR(100) NOT NULL,
4. description VARCHAR(1500) NOT NULL,
5. recommendation smallint CHECK (recommendation >0 AND recommendation <6),
6. relevance smallint CHECK (relevance >0 AND relevance <6),
7. complexity smallint CHECK (complexity >0 AND complexity <6),
8. url VARCHAR(200) NOT NULL,
9. tags varchar(210));
Abbildung 12- Datenbankschema
Sonstige Features von Micronaut
Natively Cloud Native
Die Cloud-Unterstützung von Micronaut ist direkt integriert. Das bedeutet, dass Micronaut Cloud Nativeist. Micronaut ist ein Framework mit dem sowohl Cloud- als auch On-premises Anwendungen erstellt werden können.
Die meisten heute in der JVM verwendeten Frameworks wurden vor dem Aufkommen von Cloud-Deployments und Microservice-Architekturen entwickelt. Mit diesen Frameworks erstellte Anwendungen sollten in herkömmliches Java/Java EE-Containern zum Beispiel in Java EE server, in Enterprise JavaBeans (EJB) Container oder in Application client container bereitgestellt werden. Daher wird die Cloud-Unterstützung in diesen Frameworks in der Regel eher als Add-On bereitgestellt.
Micronaut wurde von Grund auf für den Aufbau von Microservices für die Cloud entwickelt. Infolgedessen sind in Ihrer Anwendung selbst viele wichtige Funktionen verfügbar, für die normalerweise externe Bibliotheken oder Dienste erforderlich sind. Micronaut-Anwendungen sind nativ Cloud-native, um eines der derzeit beliebtesten Schlagworte der Branche zu verwenden.
Im Folgenden sind einige der Cloud-spezifischen Funktionen aufgeführt, die direkt in die Micronaut-Laufzeit integriert sind:
- Distributed Configuration
- Service Discovery
- Client-Side Load-Balancing
- Distributed Tracing
- Serverless Functions
Verteilte, dynamisch veränderbare Konfiguration
Ein weiteres Merkmal von Micronaut ist die gemeinsame Nutzung der Cloud-Konfiguration. Auf diese Weise können Konfigurationen extern gespeichert werden, sodass skalierte Instanzen eines Dienstes eine gemeinsame Konfiguration in einer Cloud-Umgebung nutzen können und Ü„nderungen direkte Auswirkungen auf alle Instanzen haben.
Abhängig von der Infrastruktur der Anwendung können beispielsweise verschiedene Beans aktiviert werden. Micronaut erkennt Umgebungen wie Android, Test, Cloud, Amazon EC2, Google Compute, Kubernetes, Heroku, Cloud Foundry, Azure oder die IBM Cloud. Neben einem internen Mechanismus zum manuellen Auflösen verteilter Konfigurationen können Dienste wie Consul oder AWS Systems Manager Parameter Store verwendet werden.
Nachrichtengesteuerte Microservices
Apache Kafka ist beliebt für die Verarbeitung verteilter Datenströme und Echtzeitdaten. Mit der Unterstützung von Micronaut für Kafka können Entwickler mit AOP-Annotationen zur Kompilierungszeit wie @KafkaClient, @KafkaListener, @Topic, @Body, @Header, @KafkaKey und einigen Zeilen der YAML-Konfiguration in nur wenigen Schritten nachrichtengesteuerte Microservices implementieren. Der Micronaut Guide enthält umfassende Anweisungen (siehe bitte https://micronaut-projects.github.io/micronaut-kafka/latest/guide/).
Micronaut JWT Authentication
Micronaut erleichtert die Konvertierung von JSON in Java-Objekte. Micronaut unterstützt die RFC 6750 Bearer Token-Spezifikationen. Micronaut stellt das JWT-Token im HTTP-Header zur Autorisierung bereit.
Zusammenfassung
Das Framework bietet einen vertrauten Entwicklungsablauf, jedoch mit minimaler Startzeit und minimalem Speicherverbrauch. Dadurch kann Micronaut in unterschiedlichen Szenarien eingesetzt werden, darunter Android-Anwendungen, serverlose Funktionen und CLI-Anwendungen.
Außerdem ist Micronaut Cloud native und stellt viele wichtige Basisfunktionalitäten zur Verfügung, für die normalerweise externe Bibliotheken oder Dienste erforderlich sind. Micronaut ist auf der Homepage gut dokumentiert. Eine kleine, aber wachsende Auswahl an Schritt-für-Schritt-Tutorials ist ebenfalls unter https://guides.micronaut.io/ verfügbar, einschließlich Leitfäden für alle drei von Micronaut unterstützten Sprachen: Java, Groovy und Kotlin. Ein schneller Einstieg ist somit gewährleistet.
1 Kommentar
Danke, Meryem, für die übersichtliche Einführung ;).