1. 概要

この記事では、EclipseMicroProfileに基づくマイクロサービスの構築に焦点を当てます。

JAX-RS、CDI、JSON-PAPIを使用してRESTfulWebアプリケーションを作成する方法を見ていきます。

2. マイクロサービスアーキテクチャ

簡単に言えば、マイクロサービスは、いくつかの独立したサービスのコレクションとして完全なシステムを形成するソフトウェアアーキテクチャスタイルです。

それぞれが1つの機能境界に焦点を合わせ、RESTなどの言語に依存しないプロトコルを使用して他の境界と通信します。

3. Eclipse MicroProfile

Eclipse MicroProfileは、マイクロサービスアーキテクチャ用にエンタープライズJavaを最適化することを目的としたイニシアチブです。 これは、Jakarta EE WebProfile APIのサブセットに基づいているため、JakartaEEアプリケーションを構築するのと同じようにMicroProfileアプリケーションを構築できます。

MicroProfileの目標は、マイクロサービスを構築するための標準APIを定義し、複数のMicroProfileランタイムにポータブルアプリケーションを提供することです。

4. Mavenの依存関係

Eclipse MicroProfileアプリケーションの構築に必要なすべての依存関係は、このBOM(部品表)依存関係によって提供されます。

<dependency>
    <groupId>org.eclipse.microprofile</groupId>
    <artifactId>microprofile</artifactId>
    <version>1.2</version>
    <type>pom</type>
    <scope>provided</scope>
</dependency>

MicroProfileランタイムにはすでにAPIと実装が含まれているため、スコープは提供として設定されます。

5. 表現モデル

簡単なリソースクラスを作成することから始めましょう。

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

ご覧のとおり、このBookクラスには注釈がありません。

6. CDIの使用

簡単に言えば、CDIは依存性注入とライフサイクル管理を提供するAPIです。 これにより、WebアプリケーションでのEnterpriseBeanの使用が簡素化されます。

次に、本の表現のストアとしてCDI管理beanを作成しましょう。

@ApplicationScoped
public class BookManager {

    private ConcurrentMap<String, Book> inMemoryStore
      = new ConcurrentHashMap<>();

    public String add(Book book) {
        // ...
    }

    public Book get(String id) {
        // ...
    }

    public List getAll() {
        // ...
    }
}

このクラスに@ApplicationScopedのアノテーションを付けます。これは、すべてのクライアントで状態が共有されるインスタンスが1つだけ必要なためです。 そのために、ConcurrentMapをタイプセーフなメモリ内データストアとして使用しました。 次に、CRUD操作のメソッドを追加しました。

これで、BeanはCDIに対応し、 @Injectアノテーションを使用してBeanBookEndpointに注入できます。

7. JAX-RS API

JAX-RSを使用してRESTアプリケーションを作成するには、@ApplicationPathで注釈が付けられたApplicationクラスと@Path。で注釈が付けられたリソースを作成する必要があります。

7.1. JAXRSアプリケーション

JAX-RSアプリケーションは、Webアプリケーションでリソースを公開するためのベースURIを識別します。

次のJAX-RSアプリケーションを作成しましょう。

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

この例では、Webアプリケーション内のすべてのJAX-RSリソースクラスが LibraryApplication に関連付けられており、同じ library パス( ApplicationPathアノテーションの値)の下にあります。

この注釈付きクラスは、JAX RSランタイムに、リソースを自動的に検出して公開する必要があることを通知します。

7.2. JAXRSエンドポイント

EndpointクラスはResourceクラスとも呼ばれ、技術的には同じタイプの多くが可能ですが、1つのリソースを定義する必要があります。

@Path で注釈が付けられた、または@Pathまたは@HttpMethodで注釈が付けられた少なくとも1つのメソッドを持つ各Javaクラスはエンドポイントです。

次に、その表現を公開するJAX-RSエンドポイントを作成します。

@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();
    }
}

この時点で、Webアプリケーションの / library /booksパスの下にあるBookEndpointリソースにアクセスできます。

7.3. JAXRSJSONメディアタイプ

JAX RSは、RESTクライアントと通信するための多くのメディアタイプをサポートしますが、 Eclipse MicroProfileは、JSOP-P APIの使用を指定するため、JSONの使用を制限します。 そのため、メソッドに @Consumes(MediaType.APPLICATION_JSON)および@ Produces(MediaType.APPLICATION_JSON)。で注釈を付ける必要があります。

@Consumes アノテーションは、受け入れられる形式を制限します。この例では、JSONデータ形式のみが受け入れられます。 HTTPリクエストヘッダーContent-Typeapplication/jsonである必要があります。

同じ考え方が@Producesアノテーションの背後にあります。 JAX RS Runtimeは、応答をJSON形式にマーシャリングする必要があります。 リクエストHTTPヘッダーAcceptapplication/json。である必要があります

8. JSON-P

JAX RS RuntimeはJSON-Pをすぐにサポートするため、JsonObjectをメソッド入力パラメーターまたは戻り型として使用できます。

しかし、現実の世界では、POJOクラスを使用することがよくあります。 したがって、JsonObjectとPOJOの間のマッピングを行う方法が必要です。 ここで、JAXRSエンティティプロバイダーが活躍します。

JSON入力ストリームをBook POJOにマーシャリングするには、 Book、タイプのパラメーターを持つリソースメソッドを呼び出し、クラス BookMessageBodyReader:を作成する必要があります。

@Provider
@Consumes(MediaType.APPLICATION_JSON)
public class BookMessageBodyReader implements MessageBodyReader<Book> {

    @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<String, String> httpHeaders, 
      InputStream entityStream) throws IOException, WebApplicationException {
 
        return BookMapper.map(entityStream);
    }
}

同じプロセスを実行して、 Book をJSON出力ストリームにアンマーシャリングします。つまり、 BookMessageBodyWriter:を作成して、戻りタイプが Book、のリソースメソッドを呼び出します。

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class BookMessageBodyWriter 
  implements MessageBodyWriter<Book> {
 
    @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<String, Object> httpHeaders, 
      OutputStream entityStream) throws IOException, WebApplicationException {
 
        JsonWriter jsonWriter = Json.createWriter(entityStream);
        JsonObject jsonObject = BookMapper.map(book);
        jsonWriter.writeObject(jsonObject);
        jsonWriter.close();
    }
}

BookMessageBodyReaderBookMessageBodyWriterには@Providerの注釈が付けられているため、これらはJAXRSランタイムによって自動的に登録されます。

9. アプリケーションの構築と実行

MicroProfileアプリケーションは移植可能であり、準拠するMicroProfileランタイムで実行する必要があります。 Open Liberty でアプリケーションをビルドして実行する方法を説明しますが、準拠している任意のEclipseMicroProfileを使用できます。

構成ファイルserver.xmlを使用してOpenLibertyランタイムを構成します。

<server description="OpenLiberty MicroProfile server">
    <featureManager>
        <feature>jaxrs-2.0</feature>
        <feature>cdi-1.2</feature>
        <feature>jsonp-1.0</feature>
    </featureManager>
    <httpEndpoint httpPort="${default.http.port}" httpsPort="${default.https.port}"
      id="defaultHttpEndpoint" host="*"/>
    <applicationManager autoExpand="true"/>
    <webApplication context-root="${app.context.root}" location="${app.location}"/>
</server>

プラグインliberty-maven-pluginをpom.xmlに追加しましょう。

<?xml version="1.0" encoding="UTF-8"?>
<plugin>
    <groupId>net.wasdev.wlp.maven.plugins</groupId>
    <artifactId>liberty-maven-plugin</artifactId>
    <version>2.1.2</version>
    <configuration>
        <assemblyArtifact>
            <groupId>io.openliberty</groupId>
            <artifactId>openliberty-runtime</artifactId>
            <version>17.0.0.4</version>
            <type>zip</type>
        </assemblyArtifact>
        <configFile>${basedir}/src/main/liberty/config/server.xml</configFile>
        <packageFile>${package.file}</packageFile>
        <include>${packaging.type}</include>
        <looseApplication>false</looseApplication>
        <installAppPackages>project</installAppPackages>
        <bootstrapProperties>
            <app.context.root>/</app.context.root>
            <app.location>${project.artifactId}-${project.version}.war</app.location>
            <default.http.port>9080</default.http.port>
            <default.https.port>9443</default.https.port>
        </bootstrapProperties>
    </configuration>
    <executions>
        <execution>
            <id>install-server</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>install-server</goal>
                <goal>create-server</goal>
                <goal>install-feature</goal>
            </goals>
        </execution>
        <execution>
            <id>package-server-with-apps</id>
            <phase>package</phase>
            <goals>
                <goal>install-apps</goal>
                <goal>package-server</goal>
            </goals>
        </execution>
    </executions>
</plugin>

このプラグインは構成可能で、一連のプロパティをスローします。

<properties>
    <!--...-->
    <app.name>library</app.name>
    <package.file>${project.build.directory}/${app.name}-service.jar</package.file>
    <packaging.type>runnable</packaging.type>
</properties>

上記のexecの目標により、実行可能なjarファイルが生成されるため、アプリケーションは、独立してデプロイおよび実行できる独立したマイクロサービスになります。 Dockerイメージとしてデプロイすることもできます。

実行可能jarを作成するには、次のコマンドを実行します。

mvn package

マイクロサービスを実行するには、次のコマンドを使用します。

java -jar target/library-service.jar

これにより、Open Libertyランタイムが開始され、サービスがデプロイされます。 エンドポイントにアクセスして、次のURLですべての書籍を入手できます。

curl http://localhost:9080/library/books

結果はJSONです。

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

1冊の本を入手するには、次のURLをリクエストします。

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

そして結果はJSONです:

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

次に、APIを操作して新しい本を追加します。

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

ご覧のとおり、応答のステータスは201であり、書籍が正常に作成されたことを示しています。 Location は、書籍にアクセスできるURIです。

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

10. 結論

この記事では、Eclipse MicroProfileに基づいて単純なマイクロサービスを構築する方法を示し、JAX RS、JSON-P、およびCDIについて説明しました。

コードはGithubから入手できます。 これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。