1概要

Java 9では、正式にはJavaプラットフォームモジュールシステム(JPMS)、または略して「モジュール」と呼ばれる、パッケージの上に新しいレベルの抽象化が導入されています。

このチュートリアルでは、新しいシステムを見ていき、さまざまな側面について説明します。

このガイドで学習するすべての概念を実証するための簡単なプロジェクトも作成します。

** 2モジュールとは

まず、モジュールの使い方を理解する前に、モジュールが何であるかを理解する必要があります。

  • モジュールは、新しいモジュール記述子ファイルとともに、密接に関連したパッケージとリソースの集まりです。

つまり、コードをさらに再利用可能にすることができる「Javaパッケージのパッケージ」抽象化です。


2.1. パッケージ

モジュール内のパッケージは、Javaの登場以来使用してきたJavaパッケージと同じです。

モジュールを作成するとき、私たちは以前に他のプロジェクトで行ったように、コードをパッケージに内部的に編成します。

私たちのコードを体系化することとは別に、パッケージはどのコードがモジュールの外で公にアクセス可能であるかを決定するために使われます。この記事の後半で、これについて詳しく説明します。


2.2. リソース

各モジュールは、メディアや設定ファイルなどのリソースに対して責任があります。

以前は、すべてのリソースをプロジェクトのルートレベルに配置し、アプリケーションのさまざまな部分に属するリソースを手動で管理していました。

モジュールを使用すると、必要な画像とXMLファイルをそれを必要とするモジュールと一緒に出荷できるので、プロジェクトを管理しやすくなります。


2.3. モジュール記述子

モジュールを作成するときに、新しいモジュールのいくつかの側面を定義する記述子ファイルを含めます。


  • Name

    – 私たちのモジュールの名前


  • Dependencies

    – このモジュールが依存している他のモジュールのリスト


  • パブリックパッケージ

    – アクセスしたいすべてのパッケージのリスト

モジュールの外側


提供されるサービス** – 可能なサービス実装を提供できます

他のモジュールによって消費される


消費されるサービス** – 現在のモジュールをaの消費者にすることができます。

サービス


Reflection Permissions ** – 他のクラスが使うことを明示的に許可する

パッケージの非公開メンバーにアクセスするためのリフレクション

モジュールの命名規則はパッケージの命名方法と似ています(ドットは許されますが、ダッシュは許されません)。プロジェクトスタイル(my.module)またはリバースDNS(

com.baeldung.mymodule

)スタイル名のどちらかを使用することは非常に一般的です。このガイドではプロジェクトスタイルを使用します。

デフォルトではすべてのパッケージがモジュール非公開であるため、公開したいすべてのパッケージをリストする必要があります。

反射についても同じことが言えます。デフォルトでは、他のモジュールからインポートしたクラスにリフレクションを使用することはできません。

この記事の後半で、モジュール記述子ファイルの使用方法の例を見ていきます。

** 2.4. モジュールの種類

新しいモジュールシステムには、4種類のモジュールがあります。


  • システムモジュール____

    – これらは実行時にリストされたモジュールです

上記の

list-modules

コマンド。それらはJava SEとJDKモジュールを含みます。


  • アプリケーションモジュール

    – これらのモジュールは私たちが通常望んでいるものです。

モジュールを使用することにしたときに構築します。それらは、アセンブルされたJARに含まれるコンパイル済みの

module-info.class

ファイルで名前が付けられ定義されています。


  • 自動モジュール

    – 追加することで非公式のモジュールを含めることができます

モジュールパスへの既存のJARファイル。モジュールの名前はJARの名前から派生します。自動モジュールは、パスによってロードされた他のすべてのモジュールへのフル読み取りアクセス権を持ちます。


  • 名前なしモジュール

    – クラスまたはJARがクラスパスにロードされたとき、

モジュールパスではなく、名前のないモジュールに自動的に追加されます。

これは、以前に作成されたJavaコードとの下位互換性を維持するための包括的なモジュールです。


2.5. 分布

モジュールは、JARファイルとして、または「展開された」コンパイル済みプロジェクトとして、2つの方法のいずれかで配布できます。もちろん、これは他のJavaプロジェクトと同じなので、驚くことではありません。

「メインアプリケーション」と複数のライブラリモジュールから構成されるマルチモジュールプロジェクトを作成できます。

JARファイルごとに1つのモジュールしか持てないため、注意が必要です。

ビルドファイルを設定するときには、プロジェクト内の各モジュールを個別のjarとしてバンドルするようにしてください。


3デフォルトモジュール

Java 9をインストールすると、JDKが新しい構造になったことがわかります。

彼らはすべてのオリジナルパッケージを受け取り、それらを新しいモジュールシステムに移しました。

これらのモジュールが何であるかは、コマンドラインに入力することでわかります。

java --list-modules

これらのモジュールは、

__ java、javafx、jdk、


and

Oracle__の4つの主要グループに分けられます。


java

モジュールはコアとなるSE言語仕様の実装クラスです。


javafx

モジュールはFX UIライブラリです。

JDK自体が必要とするものはすべて

jdk

モジュールに保存されます。

そして最後に、** Oracle固有のものはすべて

oracle

モジュールにあります。


4モジュール宣言

  • モジュールをセットアップするには、

    module-info.java

    という名前の特別なファイルをパッケージのルートに置く必要があります。

このファイルはモジュール記述子と呼ばれ、新しいモジュールを構築して使用するために必要なすべてのデータを含みます。

本体が空であるか、モジュールディレクティブで構成されている宣言を使用してモジュールを構築します。

module myModuleName {
   //all directives are optional
}

モジュール宣言は

module

キーワードで始め、その後にモジュールの名前を付けます。

このモジュールはこの宣言で動作しますが、一般的にもっと多くの情報が必要です。

それがモジュールディレクティブの登場です。


4.1. 必要

私たちの最初のディレクティブは

requires

です。このモジュールディレクティブにより、モジュールの依存関係を宣言することができます。

module my.module {
    requires module.name;
}

現在、

my.module



module.name

に対して

ランタイム依存性とコンパイル時依存性

の両方を持っています。

このディレクティブを使用すると、依存関係からエクスポートされたすべてのパブリック型に私たちのモジュールからアクセスできます。


4.2. 静的が必要です

他のモジュールを参照するコードを書くこともありますが、私たちのライブラリのユーザは決して使いたくないでしょう。

例えば、他のロギングモジュールが存在するときに内部状態をきれいに表示するユーティリティ関数を書くかもしれません。しかし、私たちのライブラリのすべての消費者がこの機能を望んでいるわけではないので、追加のロギングライブラリを含めることを望まないでしょう。

このような場合は、オプションの依存関係を使いたいと思います。

requires static

ディレクティブを使用して、コンパイル時のみの依存関係を作成します。

module my.module {
    requires static module.name;
}


4.3. 推移的なものが必要

私たちは日常生活を楽にするために図書館と協力している。

しかし、私たちのコードを取り込むモジュールがこれらの余分な「推移的な」依存関係をも取り込むことを確認する必要があります。そうしないとうまくいきません。

幸いなことに、

requires transitive

ディレクティブを使用して、ダウンストリームのコンシューマにも必要な依存関係を読み取らせることができます。

module my.module {
    requires transitive module.name;
}

開発者がmy.moduleを必要とするとき、開発者がモジュールを機能させるためにmodule.nameを要求する必要もありません。


4.4. 輸出

  • デフォルトでは、モジュールは他のモジュールにそのAPIを公開することはありません。** この

    強いカプセル化

    は、最初にモジュールシステムを作成するための重要な動機の1つでした。

私たちのコードはかなり安全ですが、それを使えるようにしたいのであれば、APIを世界中に明示的に公開する必要があります。

  • 名前付きパッケージのすべてのパブリックメンバーを公開するために

    exports

    ディレクティブを使用します。

module my.module {
    exports com.my.package.name;
}

さて、誰かがmy.moduleを必要とするとき、彼らは私たちの

com.my.package.name

パッケージのパブリックタイプにアクセスすることができますが、他のパッケージにはアクセスできません。


4.5. 輸出…​ To

パブリッククラスを世界に公開するために

exports …​ to

を使用できます。

しかし、全世界があなたのAPIにアクセスしたくない場合はどうなりますか


exports …​ to

ディレクティブを使用して、どのモジュールがAPIにアクセスできるかを制限できます。


exports

ディレクティブと同様に、パッケージをエクスポート済みとして宣言します。

しかし、このパッケージを

requires

としてインポートできるモジュールもリストしています。これがどのようなものかを見てみましょう。

module my.module {
    export com.my.package.name to com.specific.package;
}


4.6. 使用


サービス

は、他のクラスによって消費される可能性のある特定のインタフェースまたは抽象クラスの実装です。

  • モジュールが消費するサービスを

    uses

    ディレクティブで指定します。

  • クラス名we

    use

    は、実装クラスではなく、サービスのインタフェースまたは抽象クラスのいずれかです。

module my.module {
    uses class.name;
}


requires

ディレクティブと

uses

ディレクティブの間には違いがあることに注意してください。

消費したいサービスを提供するモジュールを

要求

するかもしれませんが、そのサービスはその推移的な依存関係の1つからのインタフェースを実装します。

念のため、モジュールに強制的にすべての推移的な依存関係を要求させる代わりに、

uses

ディレクティブを使用して必要なインターフェースをモジュールパスに追加します。

** 4.7. 提供するもの

  • モジュールは他のモジュールが消費できる

    サービスプロバイダ

    でも構いません

ディレクティブの最初の部分は

provides

キーワードです。これが、インターフェイスまたは抽象クラスの名前です。

次に、インターフェイスを実装するか抽象クラスを拡張する実装クラス名を指定する

with

ディレクティブがあります。

これがまとまったように見えます。

module my.module {
    provides MyInterface with MyInterfaceImpl;
}


4.8. 開いた

カプセル化がこのモジュールシステムの設計の原動力となったことを先に述べました。

Java 9より前では、リフレクションを使用して、パッケージ内のすべての型とメンバーを調べることができました。本当にカプセル化されたものは何もなく、それはライブラリの開発者にあらゆる種類の問題を引き起こす可能性があります。

Java 9は

強いカプセル化

を強制しているので、** 私たちは他のモジュールがクラスに反映するために明示的に許可を与えなければなりません。

古いバージョンのJavaのように全反射を許可し続けたい場合は、単にモジュール全体を

オープン

することができます。

open module my.module {
}


4.9. 開く

プライベート型の反映を許可する必要があるが、コード全体を公開したくない場合は、

opens

ディレクティブを使用して特定のパッケージを公開できます。

しかし、覚えておいて、これは全世界にパッケージを開くことになるので、それがあなたが望むものであることを確認してください:

module my.module {
  opens com.my.package;
}


4.10. 開く…​ To

さて、時々反射は素晴らしいですが、私達はまだ私たちが

カプセル化

から得ることができるのと同じくらい多くのセキュリティが欲しいです。 **

opens …​ to

ディレクティブを使用して、事前に承認されたモジュールのリストにパッケージを選択的に開くことができます。

module my.module {
    opens com.my.package to moduleOne, moduleTwo, etc.;
}


5コマンドラインオプション

今までに、Java 9モジュールのサポートがMavenとGradleに追加されたので、あなたはあなたのプロジェクトの多くの手動構築をする必要はありません。

ただし、コマンドラインからモジュールシステムを使用する方法を知っておくことは、まだ価値があります。

システム全体がどのように機能するのかを理解するために、以下の完全な例ではコマンドラインを使用します。



  • module-path








    – module-path

    オプションを使用して

モジュールパスこれはあなたのモジュールを含む一つ以上のディレクトリのリストです。



  • add-read


    – モジュール宣言ファイルに頼る代わりに、


requires

ディレクティブと同等のコマンドラインを使用できます。

– 追加 – 読み取り



  • add-exports








    exports

    のコマンドラインの置き換え

指令。



  • add-opens



    __ –


    モジュール内の

    open__句を置き換えます

宣言ファイル



  • add-modules



    __ –

    __モジュールのリストをのデフォルトセットに追加します。

モジュール




list-modules **

__ –

__すべてのモジュールとそのバージョンのリストを印刷する

ひも




patch-module ** – モジュール内のクラスを追加または上書きする



  • illegal-access = permit | warn | deny


    – どちらも強くリラックス

単一のグローバル警告を表示することによるカプセル化、すべての警告の表示、またはエラーによる失敗。デフォルトは

許可

です。


6. 視認性

私たちはコードの可視性について少し話をするべきです。

  • 多くのライブラリは彼らの魔法を働かせるためにリフレクションに依存しています** (JUnitとSpringは思い浮かぶ)。

Java 9のデフォルトでは、エクスポートされたパッケージ内のパブリッククラス、メソッド、およびフィールドにのみアクセスできます。リフレクションを使用して非公開メンバーにアクセスして____setAccessible(true)を呼び出しても、これらのメンバーにアクセスすることはできません。


open



opens

、および

opens …​ to

オプションを使用して、リフレクションの実行時のみのアクセスを許可できます。注意、これは実行時のみです。

私たちはプライベートタイプに対してコンパイルすることはできません、とにかくする必要はありません。

リフレクションのためにモジュールにアクセスする必要があり、そのモジュールの所有者ではない(つまり、

opens …​ to

ディレクティブを使用できない)場合は、コマンドライン

addを使用することができます。自身のモジュールが実行時にロックダウンされたモジュールにリフレクションアクセスできるようにするopens

オプション。

唯一の注意点は、これを機能させるためにモジュールを実行するために使用されるコマンドライン引数にアクセスする必要があるということです。


7. すべてを一緒に入れて

モジュールとは何か、そしてそれらをどのように使用するかを理解したので、今学んだすべての概念を実証するための簡単なプロジェクトを構築しましょう。

物事を単純にするために、MavenやGradleを使用することはありません。代わりに、私たちはモジュールを構築するためにコマンドラインツールに頼るでしょう。

** 7.1. プロジェクトの設定

まず、プロジェクト構造を設定する必要があります。ファイルを整理するためにいくつかのディレクトリを作成します。

プロジェクトフォルダを作成することから始めます。

mkdir module-project
cd module-project

これが私たちのプロジェクト全体の基盤なので、ここにMavenやGradleのビルドファイル、その他のソースディレクトリ、そしてリソースなどのファイルを追加します。

また、プロジェクト固有のモジュールをすべて保持するディレクトリを作成します。

次に、モジュールディレクトリを作成します。

mkdir simple-modules

プロジェクト構造は次のようになります。

module-project
|-//src if we use the default package
|-//build files also go at this level
|- simple-modules
  |- hello.modules
    |- com
      |- baeldung
        |- modules
          |- hello
  |- main.app
    |- com
      |- baeldung
        |- modules
          |- main


7.2. 私たちの最初のモジュール

基本構造が整ったので、最初のモジュールを追加しましょう。


__simple-modules


ディレクトリの下に、

hello.modules__という名前の新しいディレクトリを作成します。

  • 私たちはこれに好きな名前を付けることができますが、パッケージの命名規則** に従うことができます。必要に応じて、メインパッケージの名前をモジュール名として使用することもできますが、通常は、このモジュールのJARを作成するために使用するのと同じ名前を使用します。

新しいモジュールの下に、欲しいパッケージを作成することができます。今回の場合は、1つのパッケージ構造を作成します。

com.baeldung.modules.hello

次に、このパッケージに

HelloModules.java

という新しいクラスを作成します。コードを単純にします。

package com.baeldung.modules.hello;

public class HelloModules {
    public static void doSomething() {
        System.out.println("Hello, Modules!");
    }
}

そして最後に、

hello.modules

ルートディレクトリに、モジュール記述子を追加します。

module-info.java

:

module hello.modules {
    exports com.baeldung.modules.hello;
}

この例を単純にするために、私たちが行っているのは、

__ com.baeldung.modules.hello

__packageのすべての公開メンバーをエクスポートすることだけです。


7.3. 私たちの2番目のモジュール

私たちの最初のモジュールは素晴らしいですが、それは何もしません。

今それを使用する2番目のモジュールを作成できます。


simple-modules

ディレクトリの下に、

main.app

という名前の別のモジュールディレクトリを作成します。今回はモジュール記述子から始めます。

module main.app {
    requires hello.modules;
}

私たちは何も外部の世界に公開する必要はありません。代わりに、私たちがする必要があるのは私たちの最初のモジュールにかかっているだけなので、私たちはそれがエクスポートするパブリッククラスにアクセスすることができます。

これを使ってアプリケーションを作成できます。

新しいパッケージ構造を作成します:

com.baeldung.modules.main

それでは、__MainApp.javaという名前の新しいクラスファイルを作成します。

package com.baeldung.modules.main;

import com.baeldung.modules.hello.HelloModules;

public class MainApp {
    public static void main(String[]args) {
        HelloModules.doSomething();
    }
}

そして、それがモジュールをデモンストレーションするために必要なすべてのコードです。次のステップは、このコードをコマンドラインからビルドして実行することです。


7.4. 私たちのモジュールを構築する

プロジェクトをビルドするために、単純なbashスクリプトを作成してそれをプロジェクトのルートに配置することができます。


compile-simple-modules.sh

というファイルを作成します。

#!/usr/bin/env bash
javac -d outDir --module-source-path simple-modules $(find simple-modules -name "** .java")

このコマンドには、

javac

コマンドと

find

コマンドの2つの部分があります。


find

コマンドは単純に私たちのsimple-modulesディレクトリの下にあるすべての.

java

ファイルのリストを出力しています。そのリストを直接Javaコンパイラーに送ることができます。

古いバージョンのJavaとは異なる方法で行う必要がある唯一のことは、モジュールを構築していることをコンパイラに通知するための

module-source-path

パラメータを提供することです。

このコマンドを実行すると、内部に2つのコンパイル済みモジュールがある

outDir

フォルダができます。


7.5. 私たちのコードを実行する

そして、モジュールが正しく機能していることを確認するために、コードを実行します。

プロジェクトのルートに別のファイルを作成します:

run-simple-module-app.sh

#!/usr/bin/env bash
java --module-path outDir -m main.app/com.baeldung.modules.main.MainApp

モジュールを実行するには、少なくとも

module-path

とメインクラスを提供する必要があります。すべてうまくいったら、次のようになります。

>$ ./run-simple-module-app.sh
Hello, Modules!


7.6. サービスを追加する

モジュールの作り方について基本的な理解ができたので、もう少し複雑にしましょう。


provides …​ with

および

uses

ディレクティブの使用方法を見ていきます。


HelloInterface.java

という名前の

hello.modules

モジュールに新しいファイルを定義することから始めます。

public interface HelloInterface {
    void sayHello();
}

物事を簡単にするために、既存の

HelloModules.java

クラスを使用してこのインターフェースを実装します。

public class HelloModules implements HelloInterface {
    public static void doSomething() {
        System.out.println("Hello, Modules!");
    }

    public void sayHello() {
        System.out.println("Hello!");
    }
}


サービス

を作成するために必要なのはこれだけです。

今、私たちのモジュールがこのサービスを提供していることを世界に伝える必要があります。

以下を

module-info.java

に追加します。

provides com.baeldung.modules.hello.HelloInterface with com.baeldung.modules.hello.HelloModules;

ご覧のとおり、インターフェイスとそれを実装するクラスを宣言します。

次に、この

service

を消費する必要があります。

main.app

モジュールで、

module-info.java

に以下を追加しましょう。

uses com.baeldung.modules.hello.HelloInterface;

最後に、私たちの主な方法では、このサービスを次のように使用できます。

HelloModules module = new HelloModules();
module.sayHello();

コンパイルして実行します。

#> ./run-simple-module-app.sh
Hello, Modules!
Hello!

これらのディレクティブを使用して、コードがどのように使用されるかについてより明確にします。

実装をパブリックパッケージで公開しながら、インターフェイスをプライベートパッケージにすることができます。

これにより、追加のオーバーヘッドがほとんどなくなり、コードの安全性が大幅に向上します。

モジュールとそれらがどのように機能するかについてもっと学ぶために、先に進んで他のディレクティブのいくつかを試してみてください。

8.名前のないモジュールにモジュールを追加する

  • 名前のないモジュールの概念はデフォルトパッケージに似ています** したがって、実際のモジュールとは見なされませんが、デフォルトモジュールと見なすことができます。

クラスが名前付きモジュールのメンバーではない場合、自動的にこの名前なしモジュールの一部と見なされます。

場合によっては、モジュールグラフで特定のプラットフォーム、ライブラリ、またはサービスプロバイダモジュールを確実にするために、デフォルトのルートセットにモジュールを追加する必要があります。たとえば、Java 9コンパイラを使用してJava 8プログラムをそのまま実行しようとすると、モジュールを追加する必要があります。

一般に、


ルートモジュールのデフォルトセットに名前付きモジュールを追加するオプションは



__

– add-modules <モジュール>

(、<モジュール>)**

__です。 。

たとえば、すべての

java.xml.bind

モジュールへのアクセスを提供するための構文は次のようになります。

--add-modules java.xml.bind

これをMavenで使用するために、同じことを

maven-compiler-plugin

に埋め込むことができます。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.0</version>
    <configuration>
        <source>9</source>
        <target>9</target>
        <compilerArgs>
            <arg>--add-modules</arg>
            <arg>java.xml.bind</arg>
        </compilerArgs>
    </configuration>
</plugin>


9結論

この広範なガイドでは、新しいJava 9 Moduleシステムの基本に焦点を絞り、カバーしました。

私たちはモジュールとは何かについて話すことから始めました。

次に、どのモジュールがJDKに含まれているかを発見する方法について説明しました。

モジュール宣言ファイルについても詳しく説明しました。

モジュールを構築するために必要なさまざまなコマンドライン引数について説明することで、理論を完成させました。

最後に、これまでの知識をすべて実践し、モジュールシステムの上に構築された簡単なアプリケーションを作成しました。

このコードなどを見るには、https://github.com/eugenp/tutorials/tree/master/core-java-9[Githubでチェックアウトしてください]。