1. 序章

Project Jigsaw は、次の2つの側面を目的とした新機能を備えた包括的なプロジェクトです。

  • Java言語でのモジュールシステムの導入
  • およびJDKソースとJavaランタイムでの実装

この記事では、Jigsawプロジェクトとその機能を紹介し、最後に単純なモジュラーアプリケーションでまとめます。

2. モジュール性

簡単に言えば、モジュール性は、次のことを達成するのに役立つ設計原則です。

  • コンポーネント間の緩い結合
  • コンポーネント間の明確な契約と依存関係
  • 強力なカプセル化を使用した隠し実装

2.1. モジュール性の単位

ここで、モジュール性の単位は何ですか? Javaの世界では、特にOSGiでは、JARはモジュール性の単位と見なされていました。

JARは、関連するコンポーネントをグループ化するのに役立ちましたが、いくつかの制限があります。

  • JAR間の明示的なコントラクトと依存関係
  • JAR内の要素の弱いカプセル化

2.2. JAR地獄

JARには別の問題がありました-JAR地獄。 クラスパス上にあるJARの複数のバージョンにより、 ClassLoader がJARから最初に見つかったクラスをロードし、非常に予期しない結果が発生しました。

クラスパスを使用するJVMのもう1つの問題は、アプリケーションのコンパイルは成功するが、実行時にクラスパスにJARがないため、アプリケーションはClassNotFoundExceptionで実行時に失敗することでした。

2.3. モジュール性の新しい単位

これらすべての制限があるため、モジュール性の単位としてJARを使用する場合、Java言語の作成者は、モジュールと呼ばれる言語の新しい構造を思いつきました。 これにより、Java用に計画されたまったく新しいモジュラーシステムがあります。

3. プロジェクトジグソー

このプロジェクトの主な動機は次のとおりです。

  • 言語のモジュールシステムを作成– JEP261で実装
  • それをJDKソースに適用します JEP201で実装されます
  • JDK ライブラリをモジュール化– JEP200で実装
  • モジュール性をサポートするようにランタイムを更新 JEP220で実装
  • JDK のモジュールのサブセットを使用してより小さなランタイムを作成できる– JEP282で実装

もう1つの重要なイニシアチブは、内部API、sun。*パッケージおよびその他の非標準APIの下にあるJDKにカプセル化することです。 これらのAPIは、一般の人々が使用することを意図したものではなく、維持する予定もありませんでした。 しかし、これらのAPIの力により、Java開発者は、さまざまなライブラリ、フレームワーク、およびツールの開発にAPIを活用することができました。 いくつかの内部APIに置き換えが提供され、その他は内部モジュールに移動されました。

4. モジュール性のための新しいツール

  • jdeps –コードベースを分析してJDKAPIとサードパーティのJARへの依存関係を特定するのに役立ちます。 また、JDKAPIを見つけることができるモジュールの名前についても言及しています。 これにより、コードベースのモジュール化が容易になります
  • jdeprscan –非推奨のAPIの使用についてコードベースを分析するのに役立ちます
  • jlink –アプリケーションとJDKのモジュールを組み合わせることで、より小さなランタイムを作成するのに役立ちます
  • jmod –jmodファイルの操作に役立ちます。 jmodは、モジュールをパッケージ化するための新しい形式です。 この形式では、ネイティブコード、構成ファイル、およびJARファイルに収まらないその他のデータを含めることができます。

5. モジュールシステムアーキテクチャ

言語で実装されたモジュールシステムは、パッケージと同様に、これらをトップレベルの構成としてサポートします。 開発者は、コードをモジュールに編成し、それぞれのモジュール定義ファイルでそれらの間の依存関係を宣言できます。

module-info.java という名前のモジュール定義ファイルには、次のものが含まれています。

  • その名前
  • それが公に利用可能にするパッケージ
  • 依存するモジュール
  • それが消費するすべてのサービス
  • 提供するサービスの実装

上記のリストの最後の2つの項目は、一般的には使用されません。 これらは、java.util.ServiceLoaderインターフェースを介してサービスが提供および消費される場合にのみ使用されます。

モジュールの一般的な構造は次のようになります。

src
 |----com.baeldung.reader
 |     |----module-info.java
 |     |----com
 |          |----baeldung
 |               |----reader
 |                    |----Test.java
 |----com.baeldung.writer
      |----module-info.java
           |----com
                |----baeldung
                     |----writer
                          |----AnotherTest.java

上の図は、com.baeldung.readercom.baeldung.writerの2つのモジュールを定義しています。 それぞれの定義はmodule-info。javaで指定されており、コードファイルは com / baeldung /readerおよびcom/ baeldung /writerの下に配置されています。 、 それぞれ。

5.1. モジュール定義の用語

いくつかの用語を見てみましょう。 モジュールを定義するときに使用します(つまり、 module-info.java内)

  • module :モジュール定義ファイルは、このキーワードで始まり、その後に名前と定義が続きます
  • require :依存するモジュールを示すために使用されます。 このキーワードの後にモジュール名を指定する必要があります
  • 推移的 :の後に指定必要キーワード; これは、モジュールの定義に依存するモジュールを意味します推移的である必要があります <への暗黙の依存関係を取得しますモジュール名>
  • exports :公開されているモジュール内のパッケージを示すために使用されます。 このキーワードの後にパッケージ名を指定する必要があります
  • opens :実行時にのみアクセス可能で、ReflectionAPIを介したイントロスペクションにも使用できるパッケージを示すために使用されます。 これは、SpringやHibernateなどのライブラリにとって非常に重要であり、ReflectionAPIに大きく依存しています。 opens はモジュールレベルでも使用できます。その場合、実行時にモジュール全体にアクセスできます。
  • used :このモジュールが使用しているサービスインターフェイスを示すために使用されます。 タイプ名、つまり完全なクラス/インターフェース名は、このキーワードの後に指定する必要があります
  • provides…with.. .: Provides キーワードの後に識別されるサービスインターフェイスに対して、withキーワードの後に識別される実装を提供することを示すために使用されます

6. シンプルなモジュラーアプリケーション

次の図に示すように、モジュールとその依存関係を使用して単純なモジュラーアプリケーションを作成しましょう。

com.baeldung.student.modelはルートモジュールです。 モデルクラスcom.baeldung.student.model.Studentを定義します。これには、次のプロパティが含まれます。

public class Student {
    private String registrationId;
    //other relevant fields, getters and setters
}

com.baeldung.student.modelパッケージで定義されたタイプのモジュールを他のモジュールに提供します。 これは、ファイルmodule-info.javaで定義することで実現されます。

module com.baeldung.student.model {
    exports com.baeldung.student.model;
}

com.baeldung.student.service モジュールは、抽象CRUD操作を備えたインターフェイスcom.baeldung.student.service.StudentServiceを提供します。

public interface StudentService {
    public String create(Student student);
    public Student read(String registrationId);
    public Student update(Student student);
    public String delete(String registrationId);
}

com.baeldung.student.model モジュールに依存し、パッケージcom.baeldung.student.serviceで定義されたタイプを他のモジュールで使用できるようにします。

module com.baeldung.student.service {
    requires transitive com.baeldung.student.model;
    exports com.baeldung.student.service;
}

上記のモジュールの実装com.baeldung.student.service.dbimpl.StudentDbServiceを提供する別のモジュールcom.baeldung.student.service.dbimplを提供します。

public class StudentDbService implements StudentService {

    public String create(Student student) {
        // Creating student in DB
        return student.getRegistrationId();
    }

    public Student read(String registrationId) {
        // Reading student from DB
        return new Student();
    }

    public Student update(Student student) {
        // Updating student in DB
        return student;
    }

    public String delete(String registrationId) {
        // Deleting student in DB
        return registrationId;
    }
}

com.baeldung.student.service に直接依存し、 com.baeldung.student.model に推移的に依存し、その定義は次のようになります。

module com.baeldung.student.service.dbimpl {
    requires transitive com.baeldung.student.service;
    requires java.logging;
    exports com.baeldung.student.service.dbimpl;
}

最後のモジュールはクライアントモジュールです。これは、サービス実装モジュールcom.baeldung.student.service.dbimplを利用して操作を実行します。

public class StudentClient {

    public static void main(String[] args) {
        StudentService service = new StudentDbService();
        service.create(new Student());
        service.read("17SS0001");
        service.update(new Student());
        service.delete("17SS0001");
    }
}

そしてその定義は次のとおりです。

module com.baeldung.student.client {
    requires com.baeldung.student.service.dbimpl;
}

7. サンプルのコンパイルと実行

上記のモジュールをWindowsおよびUnixプラットフォーム用にコンパイルおよび実行するためのスクリプトを提供しています。 これらは、core-java-9プロジェクトここの下にあります。 Windowsプラットフォームの実行順序は次のとおりです。

  1. コンパイル-学生-モデル
  2. コンパイル-学生-サービス
  3. compile-student-service-dbimpl
  4. コンパイル-学生-クライアント
  5. run-student-client

Linuxプラットフォームの実行順序は非常に単純です。

  1. コンパイルモジュール
  2. run-student-client

上記のスクリプトでは、次の2つのコマンドライン引数を紹介します。

  • –module-source-path
  • –module-path

Java 9はクラスパスの概念を廃止し、代わりにモジュールパスを導入しています。 このパスは、モジュールを検出できる場所です。

これは、コマンドライン引数 –module-pathを使用して設定できます。

複数のモジュールを一度にコンパイルするには、 –module-source-pathを使用します。 この引数は、モジュールのソースコードの場所を提供するために使用されます。

8. JDKソースに適用されるモジュールシステム

すべてのJDKインストールには、src.zipが付属しています。 このアーカイブには、JDKJavaAPIのコードベースが含まれています。 アーカイブを抽出すると、複数のフォルダが見つかります。 java 、少数で javafx 残りは jdk。 各フォルダはモジュールを表します。

java で始まるモジュールはJDKモジュール、 javafx で始まるモジュールはJavaFXモジュール、jdkで始まるモジュールはJDKツールモジュールです。

すべてのJDKモジュールとすべてのユーザー定義モジュールは、暗黙的にjava.baseモジュールに依存します。 java.base モジュールには、Utils、Collections、IO、Concurrencyなどの一般的に使用されるJDKAPIが含まれています。 JDKモジュールの依存関係グラフは次のとおりです。

また、JDKモジュールの定義を調べて、module-info.javaでそれらを定義するための構文を理解することもできます。

9. 結論

この記事では、単純なモジュラーアプリケーションの作成、コンパイル、および実行について説明しました。 また、JDKソースコードがどのようにモジュール化されているかも確認しました。

リンカーツール(jlink)を使用してより小さなランタイムを作成したり、他の機能の中でもモジュラーjarを作成したりするなど、さらにエキサイティングな機能がいくつかあります。 これらの機能については、今後の記事で詳しく紹介します。

プロジェクトジグソーは大きな変化であり、開発者のエコシステム、特にツールやライブラリの作成者にどのように受け入れられるかを待つ必要があります。

この記事で使用されているコードは、GitHubにあります。