JDK 17の最終機能セットとして定義された14のJEPの1つであるJEP 403 (Strongly Encapsulate JDK Internals) は、sun.misc.Unsafe
などの重要な内部APIを除いたJDKすべての内部要素を強力にカプセル化する。JEP 396 (Strongly Encapsulate JDK Internals by Default) に代わって、--illegal-access
コマンドラインオプションを介しても内部APIにアクセスすることはできなくなる。
カプセル化の主な目標は、セキュリティと保守性を向上させるために、開発者が内部APIの代わりに標準APIを使用することを奨励することだ。これにより、OpenJDKに貢献する開発者は、既存のコードを壊すことなく内部コードを更新することもできる。開発者は、MavenおよびGradleプラグインでJDepsツールを利用して、既存のコードベースがJDK内部を使用しているかどうかを確認できる。
--add-opens
コマンドラインオプションは、Add-Opens JARファイルマニフェスト属性とも呼ばれ、特定のパッケージを開くために使用できる。すべての unnamed modules から java.util
モジュールへのアクセスを許可する次の例について考えてみる。
$ java --add-opens java.base/java.util=ALL-UNNAMED
--classpath {classpath} -jar {jarfile}
現在、暗号化キーなどの内部要素はリフレクションを介して利用できる。これには、sun.*
パッケージ内のすべてのクラス、メソッド、およびフィールドが含まれる。com.sun.*
、jdk.*
、および org.*
パッケージにもいくつかの内部要素が含まれており、すべての影響を受けるパッケージのリスト (内部と公開) を確認できる。リフレクションは、引き続き sun.misc
および sun.reflect
パッケージを介して使用できる。これらのパッケージは、jdk.unsupported
モジュールによって引き続き公開される。
JDK 9が強力なカプセル化を提供するモジュールシステムを導入して以来、JDKの内部APIの強力なカプセル化は過去数年間で次第に進化してきた。他のモジュールは、モジュールによって公開されたパッケージからコンパイルと実行時に、public および protected (サブクラスを介して) クラス、メソッド、および属性のみを使用できる。
2017年のリリース以降、JDK 9でサポートされたAPIは、Base64エンコードスキームのエンコーダとデコーダを取得するため静的メソッドで構成されるクラスである java.util.Base64
などの内部要素の代わりとして導入された。したがって、内部APIの呼び出しは、サポートされたAPIの呼び出しに置き換える必要がある。
JDK 9以降で追加された新しい内部要素は、デフォルトで強力にカプセル化されている。ただし、JDK 9より前に導入された内部APIは、実行時に強力にカプセル化されていない。
JDK 9では、JEP 261で最初に導入された --illegal-access
コマンドラインオプションを使用して、緩和された強力なカプセル化を通じて内部APIにアクセスすることができた。新しいデフォルトの --illegal-access=permit
は、コードがリフレクションを介して内部APIにアクセスできる名前のないモジュールに対してすべての内部コードをオープンする。--illegal-access=permit
の代わりに --illegal-access=warn
を使用すると、リフレクティブアクセスごとに警告を発行することができる。追加情報は、警告とともにスタックトレースを出力する --illegal-access=debug
で利用できる。--illegal-access=deny
を使用すると、モジュールが --add-opens
などの別のコマンドラインオプションで明示的に開かれている場合を除いて、すべてのリフレクティブアクセスがブロックされる。
JDK 16で提供されたJEP 396は、--illegal-access
のデフォルトのオプションを permit
(許可) から deny
(拒否) に変更したが、他のオプションを明示的に使用することは引き続き可能だ。JEP 396では、--illegal-access
コマンドラインオプションをすでに非推奨にして使用時に非推奨の警告が発行される。
JDK 17のリリースにより、アクセスは内部APIにさらに制限される。--illegal-access
オプションは、内部APIへのアクセスを許可しなくなる。代わりに、--add-opens
コマンドラインオプションを使用することで特定のパッケージを開くことができる。
JDK17で --illegal-access
コマンドラインオプションを、たとえば permit
で使用しようとすると、コマンドラインに警告が表示される:
$ java --illegal-access=permit {filename}.java
OpenJDK 64-Bit Server VM warning: Ignoring option --illegal-access=permit;
support was removed in 17.0
このオプションは将来完全に削除される予定だ。
ソースコードで内部APIにアクセスしようとすると、次の例外が発生する:
java.lang.reflect.InaccessibleObjectException:
Unable to make field private final {type} accessible:
module java.base does not "opens {module}" to unnamed module {module}
Javaコミュニティからいくつかの回答がある: Oracleの開発者アドボケイトであるNicolai Parlog氏は、JDK内部API使用のバックグラウンドを提供した。また、Square Open Sourceが提供するHTTPプロジェクトであるOkHttpは、JEP 403が内部APIを使用するプロジェクトにどのような影響があったかを文書化している。