違法なリフレクティブアクセスとは


126

Java 9での不正なリフレクトアクセスについては、多くの質問があります。

さて、Googleがすべて吐き出してエラーメッセージを回避しようとしているので私が見つけられないのは、実際には違法なリフレクティブアクセスです。

だから私の質問はかなり簡単です:

不正なリフレクティブアクセスの定義と警告をトリガーする状況

私は、Java 9で導入されたカプセル化の原則と関係があることを収集しましたが、それがすべて一緒にハングアップし、説明が見つからないシナリオで警告をトリガーするものは何ですか。


2
これもあなたに興味があるかもしれません:jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html
エドウィン

回答:


54

モジュールとそれらのそれぞれのパッケージ間のアクセスの理解は別として。その核心はモジュールSystem#Relaxed-strong-encapsulationにあると私は信じており、私は質問の関連部分をチェリーピックして質問に答えます。

不正なリフレクティブアクセスの定義と警告をトリガーする状況

Java-9への移行を支援するために、モジュールの強力なカプセル化を緩和できます。

  • 実装は静的アクセスを提供するかもしれません、すなわちコンパイルされたバイトコードによって。

  • 名前のないすべてのモジュールのコード化、つま​​りクラスパスのコード化に対して開いている1つ以上のモジュールの1つ以上のパッケージでランタイムシステムを呼び出す手段を提供する可能性があります。ランタイムシステムがこの方法で呼び出され、そうすることでリフレクションAPIの一部の呼び出しが成功し、それ以外の場合は失敗します。

そのような場合、純粋なモジュール式の世界ではそのようなアクセスを行うことを意図していないため、実際には「違法」であるリフレクトアクセスを作成することになります。

どのようにすべてがつながっており、どのシナリオで何が警告をトリガーしていますか?

このカプセル化の緩和は、--illegal-accessJava9ではデフォルトで等しい新しいランチャーオプションによって実行時に制御されますpermitpermitモード性を保証

このようなパッケージに対する最初のリフレクトアクセス操作では警告が発行されますが、それ以降は警告は発行されません。この単一の警告は、追加の警告を有効にする方法を説明しています。この警告は抑制できません。

モードは、値debug(メッセージとそのようなアクセスごとのスタックトレース)、warn(そのようなアクセスごとのメッセージ)、およびdeny(そのような操作を無効にする)値で構成できます。


アプリケーションをデバッグして修正することはほとんどありません:-

  • これを実行して、そのようなディレクティブ()やVM引数の明示的な使用を含むモジュール宣言なしで、あるモジュールから別のモジュールへのパッケージのオープン--illegal-access=denyを回避します。opens--add-opens
  • コンパイル済みコードからJDK内部APIへの静的参照はjdeps--jdk-internalsオプション付きのツールを使用して識別できます

不正な反射アクセス操作が検出されたときに発行される警告メッセージの形式は次のとおりです。

WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM

どこ:

$PERPETRATOR 問題のリフレクトオペレーションを呼び出したコードと、コードソース(JARファイルパス)を含む型の完全修飾名(使用可能な場合)、および

$VICTIM アクセスするメンバーを説明する文字列であり、包含タイプの完全修飾名を含みます

このようなサンプル警告の質問:= JDK9:不正なリフレクトアクセス操作が発生しました。org.python.core.PySystemState

最後に重要な注意として、このような警告に直面せず、将来的に安全であることを確認する一方で、モジュールがこれらの不正なリフレクトアクセスを行わないようにする必要があります。:)


21

Java 9モジュールシステムに関するOracleの記事があります。

デフォルトでは、モジュールの型は、それがパブリック型であり、そのパッケージをエクスポートしない限り、他のモジュールからアクセスできません。公開するパッケージのみを公開します。Java 9では、これはリフレクションにも適用されます。

https://stackoverflow.com/a/50251958/134894で指摘されてAccessibleObject#setAccessibleいるように、JDK8とJDK9の違いは有益です。具体的には、JDK9が追加されました

このメソッドは、クラスCの呼び出し元が次のいずれかが成立する場合にクラスDの宣言のメンバーへのアクセスを有効にするために使用できます。

  • CとDは同じモジュールにあります。
  • メンバーはパブリックであり、Dは、Dを含むモジュールが少なくともCを含むモジュールにエクスポートするパッケージでパブリックです。
  • メンバーは静的に保護され、DはDを含むモジュールが少なくともCを含むモジュールにエクスポートするパッケージでパブリックであり、CはDのサブクラスです。
  • Dは、Dを含むモジュールが少なくともCを含むモジュールに対して開くパッケージにあります。名前のないモジュールおよび開いているモジュールのすべてのパッケージは、すべてのモジュールに対して開いているため、Dが名前のないモジュールまたは開いているモジュールにある場合、このメソッドは常に成功します。

モジュールとそのエクスポートの重要性を強調する(Java 9の場合)


2
したがって、その記事を読んだ場合、エクスポートされたクラスのプライベートプロパティを正しく変更することはできません。保護されたプロパティとパブリックプロパティのみを変更できます。今、私はjava internalsのエクスポートについてはあまり気にしませんが、特定の値に設定するためにプライベート変数にアクセスする必要があるサードパーティのライブラリについてはもっと気にします。それ自体をモジュールとして定義する場合、それはこのスキームではもう不可能ですが、それは正しいですか?
Tschallacka

1
私はそれを直接経験していませんが、それは私の理解であり、他の場所で言及されている記事(jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html)と一緒に読むと思われますケース。–illegal-access=permit...を使用してJVMを起動する
ptomli

1
まあそれは、彼らがモジュールの方法に行くことに決めたときに、物事をいくつかの物事のために機能させることを試みることをより面白くするでしょう。超楽しい時代を先取り。
Tschallacka

1
さまざまな値fun
ptomli

もう1つの回答は、説明が多く、質問に対する回答であったため受け入れましたが、残念ながら2つの回答は受け入れられません。
Tschallacka

13

フィールドとメソッドsetAccessible()にアクセスするために使用されるメソッドを見てくださいprivate

https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-

https://docs.oracle.com/javase/9​​/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-

このメソッドが機能するために必要な条件はもっとたくさんあります。それが古いソフトウェアのほとんどすべてを壊さない唯一の理由は、プレーンJARから自動生成されたモジュールが非常に寛容であることです(すべての人のためにすべてを開いてエクスポートします)。


1

add-openオプションを使用する場合は、次のコマンドで、どのモジュールがどのパッケージを提供しているかを確認します->

java --list-modules | tr @ " " | awk '{ print $1 }' | xargs -n1 java -d

モジュールの名前は@で表示され、モジュールのないパッケージの名前は@で表示されます

注:JDK 11でテスト済み

重要:明らかに、パッケージのプロバイダーが不正アクセスを行わないよりも優れています

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.