Erstellen von Microservices mit Eclipse MicroProfile

1. Übersicht

In diesem Artikel konzentrieren wir uns auf die Erstellung eines auf Eclipse MicroProfile basierenden Mikroservices.

Wir werden uns ansehen, wie eine RESTful-Webanwendung mit JAX-RS-, CDI- und JSON-P-APIs geschrieben wird.

2. Eine Microservice-Architektur

Einfach ausgedrückt, Microservices sind ein Softwarearchitekturstil, der ein komplettes System als Sammlung mehrerer unabhängiger Dienste bildet.

Jeder konzentriert sich auf einen Funktionsumfang und kommuniziert mit den anderen über ein sprachunabhängiges Protokoll wie REST.

3. Eclipse MicroProfile

Eclipse MicroProfile ist eine Initiative zur Optimierung von Enterprise Java für die Microservices-Architektur. Es basiert auf einer Teilmenge der Jakarta EE-WebProfile-APIs, sodass wir MicroProfile-Anwendungen wie Jakarta EE-Anwendungen erstellen können.

Das Ziel von MicroProfile ist es, Standard-APIs zum Erstellen von Microservices zu definieren und tragbare Anwendungen über mehrere MicroProfile-Laufzeiten bereitzustellen.

4. Maven-Abhängigkeiten

Alle zum Erstellen einer Eclipse MicroProfile-Anwendung erforderlichen Abhängigkeiten werden von dieser Stücklistenabhängigkeit (Stückliste) bereitgestellt:

 org.eclipse.microprofile microprofile 1.2 pom provided  

Der Bereich wird wie angegeben festgelegt , da die MicroProfile-Laufzeit bereits die API und die Implementierung enthält.

5. Repräsentationsmodell

Beginnen wir mit der Erstellung einer schnellen Ressourcenklasse:

public class Book { private String id; private String name; private String author; private Integer pages; // ... }

Wie wir sehen können, gibt es keine Anmerkung zu dieser Buchklasse .

6. Verwenden von CDI

Einfach ausgedrückt ist CDI eine API, die Abhängigkeitsinjektion und Lebenszyklusmanagement bietet. Es vereinfacht die Verwendung von Enterprise-Beans in Webanwendungen.

Erstellen wir nun eine CDI-verwaltete Bean als Speicher für die Buchdarstellung:

@ApplicationScoped public class BookManager { private ConcurrentMap inMemoryStore = new ConcurrentHashMap(); public String add(Book book) { // ... } public Book get(String id) { // ... } public List getAll() { // ... } } 

Wir kommentieren diese Klasse mit @ApplicationScoped, da wir nur eine Instanz benötigen, deren Status von allen Clients gemeinsam genutzt wird. Dafür haben wir eine ConcurrentMap als typsicheren In-Memory-Datenspeicher verwendet. Dann haben wir Methoden für CRUD- Operationen hinzugefügt .

Jetzt ist unsere Bean CDI- fähig und kann mithilfe der Annotation @Inject in den Bean BookEndpoint eingefügt werden .

7. JAX-RS API

Um eine REST - Anwendung mit JAX-RS zu erstellen, benötigen wir zur Erstellung einer Anwendungsklasse kommentierte mit @ApplicationPath und einer Ressource kommentierte mit @Path.

7.1. JAX RS-Anwendung

Die JAX-RS-Anwendung identifiziert den Basis-URI, unter dem die Ressource in einer Webanwendung verfügbar gemacht wird.

Erstellen wir die folgende JAX-RS-Anwendung:

@ApplicationPath("/library") public class LibraryApplication extends Application { }

In diesem Beispiel sind alle JAX-RS-Ressourcenklassen in der Webanwendung der LibraryApplication zugeordnet, sodass sie sich unter demselben Bibliothekspfad befinden. Dies ist der Wert der ApplicationPath-Annotation.

Diese mit Anmerkungen versehene Klasse teilt der JAX RS-Laufzeit mit, dass Ressourcen automatisch gefunden und verfügbar gemacht werden sollen.

7.2. JAX RS-Endpunkt

Eine Endpunktklasse , auch Ressourcenklasse genannt , sollte eine Ressource definieren, obwohl viele der gleichen Typen technisch möglich sind.

Jede mit @Path annotierte Java-Klasse oder mindestens eine mit @Path oder @HttpMethod annotierte Methode ist ein Endpunkt.

Jetzt erstellen wir einen JAX-RS-Endpunkt, der diese Darstellung verfügbar macht:

@Path("books") @RequestScoped public class BookEndpoint { @Inject private BookManager bookManager; @GET @Path("{id}") @Produces(MediaType.APPLICATION_JSON) public Response getBook(@PathParam("id") String id) { return Response.ok(bookManager.get(id)).build(); } @GET @Produces(MediaType.APPLICATION_JSON) public Response getAllBooks() { return Response.ok(bookManager.getAll()).build(); } @POST @Consumes(MediaType.APPLICATION_JSON) public Response add(Book book) { String bookId = bookManager.add(book); return Response.created( UriBuilder.fromResource(this.getClass()) .path(bookId).build()) .build(); } } 

Zu diesem Zeitpunkt können wir auf die BookEndpoint- Ressource unter dem Pfad / library / books in der Webanwendung zugreifen .

7.3. JAX RS JSON-Medientyp

JAX RS unterstützt viele Medientypen für die Kommunikation mit REST-Clients. Eclipse MicroProfile beschränkt jedoch die Verwendung von JSON, da es die Verwendung der JSOP-P-API angibt. Daher müssen wir unsere Methoden mit @Consumes (MediaType.APPLICATION_JSON) und @ Produces (MediaType.APPLICATION_JSON) kommentieren .

Die Annotation @Consumes schränkt die akzeptierten Formate ein. In diesem Beispiel wird nur das JSON-Datenformat akzeptiert. Der HTTP-Anforderungsheader Content-Type sollte application / json sein .

Die gleiche Idee steckt hinter der Annotation @Produces . Die JAX RS Runtime sollte die Antwort auf das JSON-Format bereitstellen. Der Anforderungs-HTTP-Header Accept sollte application / json sein.

8. JSON-P

JAX RS Runtime unterstützt JSON-P ab Werk, sodass wir JsonObject als Methodeneingabeparameter oder Rückgabetyp verwenden können.

But in the real world, we often work with POJO classes. So we need a way to do the mapping between JsonObject and POJO. Here's where the JAX RS entity provider goes to play.

For marshaling JSON input stream to the Book POJO, that's invoking a resource method with a parameter of type Book, we need to create a class BookMessageBodyReader:

@Provider @Consumes(MediaType.APPLICATION_JSON) public class BookMessageBodyReader implements MessageBodyReader { @Override public boolean isReadable( Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return type.equals(Book.class); } @Override public Book readFrom( Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { return BookMapper.map(entityStream); } } 

We do the same process to unmarshal a Book to JSON output stream, that's invoking a resource method whose return type is Book, by creating a BookMessageBodyWriter:

@Provider @Produces(MediaType.APPLICATION_JSON) public class BookMessageBodyWriter implements MessageBodyWriter { @Override public boolean isWriteable( Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return type.equals(Book.class); } // ... @Override public void writeTo( Book book, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { JsonWriter jsonWriter = Json.createWriter(entityStream); JsonObject jsonObject = BookMapper.map(book); jsonWriter.writeObject(jsonObject); jsonWriter.close(); } } 

As BookMessageBodyReader and BookMessageBodyWriter are annotated with @Provider, they're registered automatically by the JAX RS runtime.

9. Building and Running the Application

A MicroProfile application is portable and should run in any compliant MicroProfile runtime. We'll explain how to build and run our application in Open Liberty, but we can use any compliant Eclipse MicroProfile.

We configure Open Liberty runtime through a config file server.xml:

  jaxrs-2.0 cdi-1.2 jsonp-1.0     

Let's add the plugin liberty-maven-plugin to our pom.xml:

  net.wasdev.wlp.maven.plugins liberty-maven-plugin 2.1.2   io.openliberty openliberty-runtime 17.0.0.4 zip  ${basedir}/src/main/liberty/config/server.xml ${package.file} ${packaging.type} false project  / ${project.artifactId}-${project.version}.war 9080 9443     install-server prepare-package  install-server create-server install-feature    package-server-with-apps package  install-apps package-server    

This plugin is configurable throw a set of properties:

  library ${project.build.directory}/${app.name}-service.jar runnable 

The exec goal above produces an executable jar file so that our application will be an independent microservice which can be deployed and run in isolation. We can also deploy it as Docker image.

To create an executable jar, run the following command:

mvn package 

And to run our microservice, we use this command:

java -jar target/library-service.jar

This will start the Open Liberty runtime and deploy our service. We can access to our Endpoint and getting all books at this URL:

curl //localhost:9080/library/books

The result is a JSON:

[ { "id": "0001-201802", "isbn": "1", "name": "Building Microservice With Eclipse MicroProfile", "author": "baeldung", "pages": 420 } ] 

To get a single book, we request this URL:

curl //localhost:9080/library/books/0001-201802

And the result is JSON:

{ "id": "0001-201802", "isbn": "1", "name": "Building Microservice With Eclipse MicroProfile", "author": "baeldung", "pages": 420 }

Now we'll add a new Book by interacting with the API:

curl -H "Content-Type: application/json" -X POST -d '{"isbn": "22", "name": "Gradle in Action","author": "baeldung","pages": 420}' //localhost:9080/library/books 

As we can see, the status of the response is 201, indicating that the book was successfully created, and the Location is the URI by which we can access it:

< HTTP/1.1 201 Created < Location: //localhost:9080/library/books/0009-201802

10. Conclusion

In diesem Artikel wurde gezeigt, wie ein einfacher Mikroservice basierend auf Eclipse MicroProfile erstellt wird, wobei JAX RS, JSON-P und CDI erläutert werden.

Der Code ist auf Github verfügbar. Da es sich um ein Maven-basiertes Projekt handelt, sollte es einfach zu importieren und so auszuführen sein, wie es ist.