web-dev-qa-db-ja.com

不正なリフレクティブアクセスとは

Java 9における違法なリフレクティブアクセスについては多くの質問があります。

グーグルがすべてのエラーメッセージを回避しようとしているために私が見つけられないのは、違法なリフレクティブアクセスが実際には何であるかということです。

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

何が違法なリフレクティブアクセスを定義し、どのような状況で警告が表示されるのですか?

私は、Java 9で導入されたカプセル化の原則と関係があることをまとめましたが、それがどのように結びついているのか、そして説明が見当たらないシナリオの中で何が警告を引き起こすのか。

52
Tschallacka

モジュールとそれぞれのパッケージ間のアクセスの理解とは別に。私はそれの核心がモジュールSystem#Relaxed-strong-encapsulationにあると信じています質問に答えてみてください。

違法な反射的アクセスの定義と警告をトリガーする状況は何ですか?

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

  • 実装は、静的アクセス、つまりコンパイル済みバイトコードを提供できます。

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

このような場合、あなたは実際に反射アクセスを作成することになりました。これは "illegal"です。そのようなアクセスを行うためのものではありませんでした。

どのようにすべてがハングし、どのシナリオで何が警告をトリガーしますか?

このカプセル化の緩和は、実行時に新しいランチャーオプション--illegal-accessによって制御されます。このオプションは、Java9ではデフォルトでpermitと等しくなります。 permitモードにより、

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

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


アプリケーションでデバッグして修正するものはほとんどありません。

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

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

    WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM
    

    ここで:

    $PERPETRATORは、問題のリフレクション操作を呼び出したコードを含む型の完全修飾名に加えて、使用可能な場合はコードソース(つまり、JARファイルパス)です。

    $VICTIMは、アクセスされるメンバーを記述する文字列で、囲んでいる型の完全修飾名を含みます

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

最後の重要な注意点として、このような警告に直面せず、将来的に安全であることを保証しようとする一方で、あなたが行う必要があるのは、モジュールがそれらの違法な反射アクセスを行わないことです。 :)

22
Naman

Java 9モジュールシステムに関して見つけたOracle 記事 があります

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

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

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

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

これは、モジュールとそのエクスポートの重要性を強調しています(Java 9)。

14
ptomli

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

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から自動生成されたモジュールが非常に寛容だということです(すべての人に開いてエクスポートする)。

10
user158037