12.

Java Project Jigsaw(モジュールシステム)完全ガイド — JPMS / JLink / jdeps

編集
この記事の要点
  • Project Jigsaw は Java 9 で導入されたモジュールシステム (JPMS)module-info.java でモジュール境界を宣言する
  • requires (依存)、exports (公開)、opens (リフレクション許可) の 3 ディレクティブが基本
  • 強カプセル化により com.sun.* など内部 API へのアクセスが制限され、レガシーコードが動かなくなる事例多発
  • JLink で必要モジュールだけを含むカスタム JRE を作成可能 → Docker イメージサイズを大幅削減
  • jdeps --print-module-deps で既存 JAR の依存モジュールを解析。マイグレーションの第一歩

Project Jigsaw とは

Project Jigsaw は Java 7 時代から構想されていた Java プラットフォーム自体のモジュール化計画です。最終的に Java 9 (2017 年 9 月)JPMS (Java Platform Module System) として正式リリースされました。これにより JDK / JRE 自体が約 75 個のモジュールに分割され、アプリケーションも独自モジュールを宣言できるようになりました。

解決しようとした問題

  • JAR Hell — クラスパス上で同名クラスが衝突しても警告すらない
  • 強い隠蔽がないpublic なクラスは全世界から呼べる (com.sun.* 問題)
  • JRE が肥大 — 200 MB 超の rt.jar が必須。組込/コンテナで重い
  • 遅延ロード不可 — 必要なクラスだけ読み込む仕組みがない
  • 依存関係が暗黙 — JAR 内に「何に依存するか」のメタ情報がない

module-info.java の基本

各モジュールのルートに module-info.java を置きます:

module com.example.app {
    // 依存するモジュール
    requires java.sql;
    requires java.net.http;
    requires transitive com.example.commons;   // 自モジュール利用者にも公開

    // 外部に公開するパッケージ
    exports com.example.app.api;
    exports com.example.app.spi to com.example.plugin;   // 限定公開

    // リフレクションを許可するパッケージ (Spring/Jackson 等が要求)
    opens com.example.app.entity to com.fasterxml.jackson.databind;
    opens com.example.app.config;   // 全モジュールにリフレクション開放

    // サービス利用 / 提供 (ServiceLoader)
    uses java.sql.Driver;
    provides com.example.app.spi.Plugin
        with com.example.app.impl.DefaultPlugin;
}

3 つのキーディレクティブ

ディレクティブ意味
requires他モジュールへの依存宣言requires java.sql;
requires transitive依存を推移的に公開requires transitive java.xml;
exportsパッケージを外部に公開exports com.example.api;
exports ... to特定モジュールにのみ公開exports com.x to com.y;
opensリフレクションを許可opens com.example.entity;
usesServiceLoader で使用uses java.sql.Driver;
provides ... withサービス実装を提供provides Foo with FooImpl;

強カプセル化と互換性問題

Java 9 で sun.misc.Unsafe 等の内部 API が原則アクセス不可になり、大量のライブラリが動かなくなりました。典型エラー:

java.lang.IllegalAccessError: class com.example.Foo cannot access class
sun.misc.Unsafe (in module java.base) because module java.base does not
export sun.misc to unnamed module

緊急回避は --add-opens / --add-exports オプション:

java --add-opens java.base/java.lang=ALL-UNNAMED \
     --add-opens java.base/java.util=ALL-UNNAMED \
     --add-exports java.base/sun.security.x509=ALL-UNNAMED \
     -jar myapp.jar

恒久対応は該当ライブラリを Jigsaw 対応の新バージョンにアップグレードすること。

JLink — カスタム JRE 作成

Jigsaw の真価が JLink です。必要モジュールだけを含む軽量 JRE イメージを作れます:

# 1. アプリのモジュール依存を解析
jdeps --print-module-deps --ignore-missing-deps myapp.jar
# → java.base,java.logging,java.sql,java.xml

# 2. JLink で必要モジュールだけのカスタム JRE を作成
jlink --add-modules java.base,java.logging,java.sql,java.xml \
      --output myjre \
      --compress=2 \
      --no-header-files \
      --no-man-pages \
      --strip-debug

# 3. 結果は約 40 MB (フル JRE の 200 MB → 1/5)
du -sh myjre/
# 41M    myjre/

# 4. カスタム JRE で実行
./myjre/bin/java -jar myapp.jar

Docker イメージで FROM openjdk:17 (約 470 MB) を FROM debian + JLink (約 90 MB) に縮小できます。

jdeps による移行解析

既存 JAR を Jigsaw 化する第一歩:

# JDK 依存モジュールの一覧
jdeps --jdk-internals myapp.jar      # 内部 API 使用箇所
jdeps --print-module-deps myapp.jar  # 依存モジュール名のみ
jdeps -recursive --module-path libs myapp.jar   # 推移的依存

# module-info.java 雛形を生成
jdeps --generate-module-info /tmp/modules myapp.jar
# → /tmp/modules/myapp/module-info.java

Automatic Module

module-info.java を持たない JAR を --module-path に置くと Automatic Module として扱われ、ファイル名から自動命名されます:

  • commons-lang3-3.14.0.jar → org.apache.commons.lang3 (Automatic-Module-Name manifest)
  • Automatic-Module-Name が無い場合はファイル名から推測 (推奨されない)

Automatic Module はすべてを exports / requires する緩い扱い。移行の橋渡しとして用意されています。

Maven / Gradle との統合



    org.apache.maven.plugins
    maven-compiler-plugin
    3.13.0
    
        17
    




    org.apache.maven.plugins
    maven-jar-plugin
    
        
            
                com.example.lib
            
        
    

Spring Boot と Jigsaw

Spring Boot 3.x は完全に Jigsaw に対応しているわけではなく、fat JAR + Automatic Module運用が標準。Spring Framework 自体は module-info を持ちますが、利用者側で完全モジュール化するメリットは限定的です。マイクロサービスで JLink + 軽量イメージを作りたい場合のみ採用する価値があります。

Project Jigsaw が変えたこと

  • com.sun.* / sun.misc.Unsafe 等の内部 API が原則使用不可に
  • JRE 自体が JLink で組み立てる構成に (Java 11 以降は JRE 単体配布廃止)
  • 大量のリフレクション系ライブラリ (Hibernate, Jackson 等) が opens を要求するように
  • Spring の field injection で警告が出るプロジェクトも
  • マイクロサービスのコンテナイメージサイズ最適化に貢献

FAQ

Q: 既存プロジェクトをすぐ Jigsaw 化するべき?
A: 強制ではありません。classpath モード (unnamed module) でも Java 17/21 は動作します。マイクロサービスで軽量化したいときに JLink から導入するのが現実的。

Q: split package とは?
A: 同じパッケージが複数モジュールに分かれている状態。Jigsaw では禁止。古い JAR で頻発するため、リネームか合併が必要。

Q: --add-opens を本番で使ってもいい?
A: 短期的には可。ただし将来の Java バージョンで効かなくなる可能性があるため恒久対応はライブラリのアップグレード

関連項目

  • JEP 261 — Module System
  • JEP 282 — JLink
  • jdeps — モジュール依存解析ツール
  • OSGi — Java 古来のモジュールシステム。Jigsaw とは別物
編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. プラットホーム
  2. 環境構築
  3. 文法
  4. API
  5. Servlet(サーブレット)
  6. JSP
  7. Applet(アプレット)
  8. デザインパターン
  9. フレームワーク
  10. ライブラリ
  11. Androidアプリケーション
  12. Project Jigsaw
  13. エラー一覧
  14. 日付の加算、減算
  15. 文字列の数字チェック
  16. 改行コードの削除
  17. 先頭と末端の文字の削除
  18. warファイルの中身を確認する方法
  19. nullもしくは空文字の判定
  20. beanの中身を確認する方法
  21. org.apache.log4j.Logger のログ出力で printStackTrace() のエラー内容を出力する方法
  22. Javaのバージョン確認方法