サイトアイコンOmomuki Tech

Jakarta EE徹底解説:JAX-RSとCDIで作るモダンWeb API開発

この記事から得られる知識

この記事を通じて、以下の知識を習得し、Jakarta EEを使った開発をスタートできるようになります。

  • Jakarta EEの基本的な概念と、Java EEからの歴史的背景。
  • Jakarta RESTful Web Services (JAX-RS) を用いたRESTful APIエンドポイントの構築方法。
  • Jakarta Contexts and Dependency Injection (CDI) を活用した、柔軟で保守性の高いコンポーネント管理手法。
  • JAX-RSとCDIを組み合わせた、実践的なWeb APIの実装パターン。
  • Mavenを利用したJakarta EEプロジェクトの基本的な環境構築手順。
  • Jakarta EE 10で導入されたCore Profileや、Jakarta EE 11の展望といった最新動向。

第1章: Jakarta EEとは?エンタープライズJavaの新たな夜明け

Jakarta EEは、長年にわたりエンタープライズJavaの世界を牽引してきたJava EE (Java Platform, Enterprise Edition) の後継となる技術仕様群です。 2017年にOracle社からEclipse Foundationへ移管されたことを機に、名称が変更されました。この移管は、よりオープンでコミュニティ主導の開発プロセスへの移行を意味し、エンタープライズJavaの未来にとって重要な一歩となりました。

Java EEからの移行とパッケージ名の変更

Jakarta EEへの移行における最も大きな技術的変更点は、APIのパッケージ名前空間の変更です。 従来のJava EEではjavax.*というパッケージ名が使われていましたが、これはOracle社が商標権を保持しているため、Eclipse Foundationが自由に拡張することができませんでした。

この問題を解決するため、Jakarta EE 9では、パッケージ名がjavax.*からjakarta.*へ全面的に変更されました。これは「ビッグバン」とも呼ばれる破壊的な変更であり、既存のJava EEアプリケーションをJakarta EE 9以降へ移行する際には、ソースコードや設定ファイルの修正が必須となります。

ポイント: Jakarta EE 8まではjavax.*パッケージが使用されていましたが、Jakarta EE 9以降はjakarta.*パッケージへの変更が必要です。この変更に対応するための移行ツールも提供されています。

進化するバージョンとプロファイル

Jakarta EEは活発な開発が続けられており、バージョンアップごとに新たな機能が追加されています。

  • Jakarta EE 8 (2019年9月リリース): Java EE 8との完全な互換性を持ち、パッケージ名もjavax.*のままでした。事実上、Java EE 8のブランド名を変更したリリースです。
  • Jakarta EE 9 (2020年12月リリース): 最大のトピックは前述の通り、パッケージ名をjakarta.*へ変更したことです。機能的な追加はありませんでした。
  • Jakarta EE 9.1 (2021年5月リリース): Java SE 11のサポートを追加しました。
  • Jakarta EE 10 (2022年9月リリース): パッケージ名変更後、初の本格的な機能追加リリースです。後述するCore Profileが新たに導入され、クラウドネイティブなアプリケーション開発への対応が強化されました。

また、Jakarta EEはアプリケーションの用途に応じて、使用する仕様をまとめた「プロファイル」を定義しています。

プロファイル名概要主な用途
Platform ProfileJakarta EEの全ての仕様を含むフルセットのプロファイルです。伝統的な多機能エンタープライズアプリケーション。
Web ProfileWebアプリケーション開発に必要な基本的な仕様(Servlet, JAX-RS, CDI, JPAなど)に絞ったサブセットです。一般的なWebアプリケーション、RESTful APIサービス。
Core ProfileJakarta EE 10で新設された、最も軽量なプロファイルです。 CDI Lite, JAX-RS, JSON-B/Pなど、マイクロサービスやクラウドネイティブ開発に不可欠な仕様のみで構成されています。マイクロサービス、サーバレス関数、GraalVMネイティブイメージ化を想定したアプリケーション。

第2章: 開発環境の準備

Jakarta EEアプリケーションの開発を始めるために、いくつかのツールを準備しましょう。ここでは、ビルドツールとして広く使われているMavenをベースに解説します。

  • Java Development Kit (JDK): Jakarta EE 10はJava SE 11以降を要求します。 Java SE 17や最新のLTS版を利用することを推奨します。
  • Maven: Javaプロジェクトのビルドと依存関係管理を行うツールです。
  • IDE (統合開発環境): IntelliJ IDEA, Eclipse, Visual Studio Codeなどが人気です。
  • Jakarta EE互換アプリケーションサーバー: 開発したアプリケーションを実行するためのサーバーです。代表的なものにWildFly, Open Liberty, Payara, GlassFishなどがあります。

Mavenプロジェクトのセットアップ (pom.xml)

MavenプロジェクトでJakarta EEを利用するには、pom.xmlファイルに適切な依存関係を追加します。Jakarta EE 10のWeb Profileを利用する場合、以下のようにjakarta.jakartaee-web-apiへの依存関係を記述します。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>jakartaee-sample</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <jakartaee.version>10.0.0</jakartaee.version> </properties> <dependencies> <!-- Jakarta EE Web Profile API --> <dependency> <groupId>jakarta.platform</groupId> <artifactId>jakarta.jakartaee-web-api</artifactId> <version>${jakartaee.version}</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.3.2</version> </plugin> </plugins> </build>
</project>
scope=providedとは?
この設定は、依存関係(ライブラリ)がコンパイル時にのみ必要で、実行時にはアプリケーションサーバーから提供されることを示します。これにより、作成されるWARファイルのサイズを小さく保つことができます。

第3章: Jakarta RESTful Web Services (JAX-RS) によるAPIエンドポイントの作成

Jakarta RESTful Web Services (JAX-RS) は、RESTアーキテクチャスタイルに準拠したWebサービスをJavaで開発するためのAPI仕様です。アノテーションを駆使することで、POJO (Plain Old Java Object) を簡単にWebリソースとして公開できます。

JAX-RSの基本アノテーション

JAX-RSの核となるのは、振る舞いを定義するアノテーション群です。

アノテーション説明
@Pathリソースクラスやメソッドが処理するURIパスを指定します。クラスとメソッドの両方に付与でき、パスが連結されます。
@GET, @POST, @PUT, @DELETEそれぞれHTTPメソッドのGET, POST, PUT, DELETEに対応するメソッドを定義します。
@Producesレスポンスとして生成するデータのMIMEタイプ(例: "application/json")を指定します。
@Consumesリクエストとして受け入れるデータのMIMEタイプを指定します。
@PathParamURIパスの一部をパラメータとして受け取ります。例: /users/{id}id部分。
@QueryParamURIのクエリ文字列をパラメータとして受け取ります。例: /users?name=taroname部分。

シンプルなAPIの実装例

まず、JAX-RSを有効化するための設定クラスを作成します。このクラスはjakarta.ws.rs.core.Applicationを継承し、@ApplicationPathアノテーションでAPI全体のベースパスを定義します。

package com.example.jakartaee_sample;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
// このアプリケーションのREST APIのベースパスを "/api" に設定
@ApplicationPath("/api")
public class HelloApplication extends Application {
}

次に、実際のエンドポイントとなるリソースクラスを作成します。

package com.example.jakartaee_sample.resources;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/hello") // このクラスは "/api/hello" へのリクエストを処理
public class HelloResource { @GET // HTTP GETメソッドに対応 @Produces(MediaType.TEXT_PLAIN) // レスポンスはプレーンテキスト public String sayHello() { return "Hello, Jakarta EE!"; } @GET @Path("/{name}") // "/api/hello/Taro" のようなリクエストを処理 @Produces(MediaType.TEXT_PLAIN) public String sayHelloToName(@PathParam("name") String name) { return "Hello, " + name + "!"; }
}

このコードをデプロイすると、以下のエンドポイントにアクセスできるようになります。

  • GET /jakartaee-sample/api/helloHello, Jakarta EE!という文字列を返す。
  • GET /jakartaee-sample/api/hello/WorldHello, World!という文字列を返す。

第4章: Jakarta Contexts and Dependency Injection (CDI) による依存性注入

Jakarta Contexts and Dependency Injection (CDI) は、コンポーネント間の依存関係を疎結合にするための仕様です。 「依存性の注入 (Dependency Injection, DI)」というデザインパターンをJavaで簡単に実現できます。CDIを使うことで、コンポーネントのライフサイクル管理やインスタンスの注入をコンテナに任せることができ、ビジネスロジックの実装に集中できます。

CDIの主要なアノテーション

アノテーション説明
@ApplicationScopedアプリケーション全体で単一のインスタンスを共有します。アプリケーションの起動時に生成され、終了時に破棄されます。
@RequestScopedHTTPリクエストごとに新しいインスタンスが生成され、レスポンスが返されると破棄されます。Webアプリケーションで最もよく利用されるスコープの一つです。
@SessionScopedHTTPセッションごとにインスタンスが生成されます。ログイン情報など、セッション中保持したい情報を管理するのに使われます。
@Dependent注入先のコンポーネントと同じライフサイクルを持つ、デフォルトのスコープです。
@Inject指定したフィールドやコンストラクタ、メソッドに、CDIコンテナが管理するインスタンスを注入するよう指示します。
@Produces特定の型のインスタンスを生成するメソッドを定義します。これにより、フレームワークが直接管理できないクラスのインスタンスもDIの対象にできます。

CDIビーンの作成と注入

CDIによって管理されるJavaオブジェクトを「CDIビーン」と呼びます。スコープアノテーションを付与するだけで、どんなクラスもCDIビーンにすることができます。

例えば、メッセージを生成するシンプルなサービスクラスを作成してみましょう。

package com.example.jakartaee_sample.services;
import jakarta.enterprise.context.ApplicationScoped;
import java.time.LocalDateTime;
// アプリケーションスコープのCDIビーンとして定義
@ApplicationScoped
public class GreetingService { public String createGreetingMessage(String name) { return "Hello, " + name + "! Welcome to the world of CDI. The time is " + LocalDateTime.now(); }
}

このGreetingServiceを利用するには、@Injectアノテーションを使います。CDIはJAX-RSとシームレスに連携するため、JAX-RSリソースクラスに直接サービスを注入できます。

package com.example.jakartaee_sample.resources;
import com.example.jakartaee_sample.services.GreetingService;
import jakarta.inject.Inject; // @Injectアノテーションをインポート
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/greeting")
public class GreetingResource { // CDIコンテナがGreetingServiceのインスタンスをここに注入する @Inject private GreetingService greetingService; @GET @Produces(MediaType.TEXT_PLAIN) public String getGreeting() { // 注入されたサービスを利用してビジネスロジックを実行 return greetingService.createGreetingMessage("Guest"); }
}
疎結合のメリット:
GreetingResourceGreetingServiceの具体的なインスタンス生成方法を知る必要がありません。 単に「GreetingServiceが必要だ」と@Injectで宣言するだけです。これにより、将来GreetingServiceの実装を変更したり、テスト時にモックに差し替えたりすることが非常に容易になります。これがDIがもたらす大きな利点です。

第5章: JAX-RSとCDIの連携実践 – ユーザー管理APIの作成

これまでの知識を組み合わせて、より実践的な例として、簡単なユーザー管理APIを作成してみましょう。このAPIは、インメモリのリストでユーザー情報を管理し、JSON形式でデータのやり取りを行います。

Step 1: データモデル (エンティティ) の作成

まず、ユーザー情報を保持するシンプルなPOJOを作成します。Jakarta EEには、JSONとの間でJavaオブジェクトをマッピングするためのJakarta JSON Binding (JSON-B) 仕様が含まれており、特別な設定なしでPOJOをJSONに変換できます。

package com.example.jakartaee_sample.model;
// シンプルなユーザーデータクラス
public class User { private Long id; private String name; private String email; public User() { } public User(Long id, String name, String email) { this.id = id; this.name = name; this.email = email; } // GetterとSetterを定義(JSON-Bが利用するため必須) public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; }
}

Step 2: ビジネスロジック (CDIサービス) の作成

次に、ユーザーデータの管理ロジックを担当するCDIビーンを作成します。ここではインメモリでデータを管理しますが、実際のアプリケーションではデータベースと連携する層になります。

package com.example.jakartaee_sample.services;
import com.example.jakartaee_sample.model.User;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
@ApplicationScoped // このサービスはアプリケーション全体でシングルトン
public class UserService { // スレッドセーフなリストでユーザーを管理 private final List<User> users = new CopyOnWriteArrayList<>(); private final AtomicLong sequence = new AtomicLong(0); public UserService() { // 初期データ addUser("Taro Yamada", "taro@example.com"); addUser("Hanako Suzuki", "hanako@example.com"); } public List<User> findAll() { return users; } public Optional<User> findById(Long id) { return users.stream().filter(u -> u.getId().equals(id)).findFirst(); } public User addUser(String name, String email) { User newUser = new User(sequence.incrementAndGet(), name, email); users.add(newUser); return newUser; }
}

Step 3: APIエンドポイント (JAX-RSリソース) の作成

最後に、UserServiceを注入したJAX-RSリソースを作成し、HTTPリクエストに応じて適切なサービスメソッドを呼び出します。

package com.example.jakartaee_sample.resources;
import com.example.jakartaee_sample.model.User;
import com.example.jakartaee_sample.services.UserService;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.List;
@Path("/users") // ベースパスは /api/users
@Produces(MediaType.APPLICATION_JSON) // デフォルトのレスポンス形式はJSON
@Consumes(MediaType.APPLICATION_JSON) // デフォルトのリクエスト形式はJSON
public class UserResource { @Inject private UserService userService; @GET public List<User> getAllUsers() { return userService.findAll(); } @GET @Path("/{id}") public Response getUserById(@PathParam("id") Long id) { return userService.findById(id) .map(user -> Response.ok(user).build()) // ユーザーが見つかった場合 .orElse(Response.status(Response.Status.NOT_FOUND).build()); // 見つからない場合 } @POST public Response createUser(User user) { // 本来は入力チェックなどが必要 User createdUser = userService.addUser(user.getName(), user.getEmail()); return Response.status(Response.Status.CREATED).entity(createdUser).build(); }
}

これで、RESTfulなユーザー管理APIが完成しました。@Produces(MediaType.APPLICATION_JSON)を指定するだけで、List<User>Userオブジェクトが自動的にJSON形式の文字列に変換されてクライアントに返却されます。これがJakarta EEの標準仕様に準拠することの強力なメリットです。


第6章: Jakarta EEの未来と展望

Jakarta EEは、クラウドネイティブ、マイクロサービスといった現代的なアーキテクチャの要求に応えるため、進化を続けています。

Jakarta EE 10とCore Profile

前述の通り、2022年9月にリリースされたJakarta EE 10は、Core Profileを導入した画期的なバージョンです。これにより、開発者はアプリケーションの要件に合わせて、必要な機能だけを含む軽量なランタイムを構築できるようになりました。 これは、高速な起動時間と低いメモリ消費が求められるマイクロサービス環境において、Javaの競争力を高める重要な一歩です。

Jakarta EE 11と今後の方向性

現在開発が進められているJakarta EE 11では、さらなるモダン化が計画されています。 コミュニティで議論されている主要なテーマには以下のようなものがあります。

  • Java SE 21サポート: 最新のJava LTSバージョンへの対応。これには、Project Loomで導入された仮想スレッド(Virtual Threads)の活用が含まれる可能性があり、スループットの大幅な向上が期待されます。
  • Jakarta Data: Spring Dataにインスパイアされた、新しいデータアクセス仕様です。 リポジトリインターフェースを定義するだけで、定型的なデータアクセスコードを自動生成することを目指しており、開発効率の劇的な向上が見込まれています。
  • CDIのさらなる活用: 各仕様間の連携をより一層CDI中心に据えることで、プラットフォーム全体の一貫性と使いやすさを向上させる動きがあります。

Jakarta EEは、標準仕様に基づいた安定性と移植性、そしてベンダーニュートラルなオープンコミュニティによる活発な開発という強みを持ち、これからもエンタープライズJavaの世界で中心的な役割を担っていくことでしょう。


まとめ

Jakarta EEは、標準仕様に準拠することで高いポータビリティを確保しつつ、CDIのような強力なプログラミングモデルによって、生産性と保守性の高いアプリケーション開発を可能にします。Core Profileの登場やJakarta EE 11で計画されている新機能は、このプラットフォームがクラウドネイティブ時代においても進化し続ける力強い証拠です。

この記事が、皆さんがJakarta EEの世界へ足を踏み入れるための一助となれば幸いです。

モバイルバージョンを終了