Was ist ein illegaler reflektierender Zugang?

126

Es gibt viele Fragen zum illegalen reflektierenden Zugriff in Java 9.

Was ich jetzt nicht finden kann, weil Google nur Leute anspricht, die versuchen, die Fehlermeldungen zu umgehen, ist, was ein illegaler reflektierender Zugriff tatsächlich ist.

Meine ziemlich einfache Frage lautet also:

Was definiert einen illegalen reflektierenden Zugang und welche Umstände lösen die Warnung aus?

Ich habe festgestellt, dass es etwas mit den in Java 9 eingeführten Kapselungsprinzipien zu tun hat, aber wie alles zusammen hängt und was die Warnung in welchem ​​Szenario auslöst, für das ich keine Erklärung finden kann.

Tschallacka
quelle
2
Dies könnte Sie auch interessieren: jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html
Edwin

Antworten:

53

Abgesehen von einem Verständnis der Zugriffe zwischen Modulen und ihren jeweiligen Paketen. Ich glaube, der Kern davon liegt im Modulsystem # Relaxed-strong-encapsulation, und ich würde nur die relevanten Teile davon auswählen, um zu versuchen, die Frage zu beantworten.

Was definiert einen illegalen reflektierenden Zugang und welche Umstände lösen die Warnung aus?

Um die Migration auf Java-9 zu unterstützen, könnte die starke Kapselung der Module gelockert werden.

  • Eine Implementierung kann statischen Zugriff bereitstellen , dh durch kompilierten Bytecode.

  • Kann eine Möglichkeit bieten, sein Laufzeitsystem mit einem oder mehreren Paketen eines oder mehrerer seiner Module aufzurufen, die für den Code in allen unbenannten Modulen geöffnet sind , dh für den Code im Klassenpfad. Wenn das Laufzeitsystem auf diese Weise aufgerufen wird und wenn dadurch einige Aufrufe der Reflection-APIs erfolgreich sind, wo sie sonst fehlgeschlagen wären.

In solchen Fällen haben Sie tatsächlich einen reflektierenden Zugriff vorgenommen, der "illegal" ist, da Sie in einer rein modularen Welt solche Zugriffe nicht durchführen sollten.

Wie hängt alles zusammen und was löst in welchem ​​Szenario die Warnung aus?

Diese Lockerung der Kapselung wird zur Laufzeit durch eine neue Launcher-Option gesteuert, --illegal-accessdie in Java9 standardmäßig gleich ist permit. Der permitModus sorgt dafür

Die erste Operation mit reflektierendem Zugriff auf ein solches Paket bewirkt, dass eine Warnung ausgegeben wird, aber nach diesem Punkt werden keine Warnungen ausgegeben. Diese einzelne Warnung beschreibt, wie Sie weitere Warnungen aktivieren. Diese Warnung kann nicht unterdrückt werden.

Die Modi können mit Werten debug(Nachricht sowie Stapelverfolgung für jeden solchen Zugriff), warn(Nachricht für jeden solchen Zugriff) und deny(deaktiviert solche Operationen) konfiguriert werden .


Einige Dinge, die zum Debuggen und Beheben von Anwendungen erforderlich sind, sind:

  • Führen Sie es mit aus --illegal-access=deny, um zu erfahren, wie Sie Pakete von einem Modul zum anderen öffnen können , ohne eine Moduldeklaration mit einer solchen Direktive ( opens) oder einer expliziten Verwendung von--add-opens VM arg.
  • Statische Verweise von kompiliertem Code auf JDK-interne APIs konnten mithilfe des jdepsTools mit der --jdk-internalsOption identifiziert werden

Die Warnmeldung, die ausgegeben wird, wenn eine illegale Operation mit reflektierendem Zugriff erkannt wird, hat die folgende Form:

WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM

wo:

$PERPETRATOR ist der vollständig qualifizierte Name des Typs, der den Code enthält, der die betreffende Reflexionsoperation aufgerufen hat, sowie die Codequelle (dh JAR-Dateipfad), falls verfügbar, und

$VICTIM ist eine Zeichenfolge, die das Mitglied beschreibt, auf das zugegriffen wird, einschließlich des vollständig qualifizierten Namens des umschließenden Typs

Fragen zu einer solchen Beispielwarnung: = JDK9: Es ist eine unzulässige reflektierende Zugriffsoperation aufgetreten. org.python.core.PySystemState

Der letzte und wichtige Hinweis: Während Sie sicherstellen möchten, dass Sie nicht vor solchen Warnungen stehen und zukunftssicher sind, müssen Sie lediglich sicherstellen, dass Ihre Module keine illegalen reflektierenden Zugriffe ausführen. :) :)

Naman
quelle
21

Ich habe einen Oracle- Artikel zum Java 9-Modulsystem gefunden

Standardmäßig ist ein Typ in einem Modul für andere Module nur zugänglich, wenn es sich um einen öffentlichen Typ handelt und Sie sein Paket exportieren. Sie legen nur die Pakete offen, die Sie verfügbar machen möchten. Bei Java 9 gilt dies auch für die Reflexion.

Wie unter https://stackoverflow.com/a/50251958/134894 ausgeführt , sind die Unterschiede zwischen AccessibleObject#setAccessibleJDK8 und JDK9 aufschlussreich. Insbesondere JDK9 hinzugefügt

Diese Methode kann von einem Aufrufer in Klasse C verwendet werden, um den Zugriff auf ein Mitglied der deklarierenden Klasse D zu ermöglichen, wenn eine der folgenden Bedingungen erfüllt ist:

  • C und D befinden sich im selben Modul.
  • Das Mitglied ist öffentlich und D ist öffentlich in einem Paket, das das Modul mit D in mindestens das Modul mit C exportiert.
  • Das Mitglied ist statisch geschützt, D ist in einem Paket öffentlich, das das Modul mit D in mindestens das Modul mit C exportiert, und C ist eine Unterklasse von D.
  • D befindet sich in einem Paket, das das Modul mit D mindestens für das Modul mit C öffnet. Alle Pakete in unbenannten und offenen Modulen sind für alle Module offen. Daher ist diese Methode immer erfolgreich, wenn sich D in einem unbenannten oder offenen Modul befindet.

Dies unterstreicht die Bedeutung von Modulen und deren Exporten (in Java 9).

ptomli
quelle
2
Wenn ich diesen Artikel also richtig lese, ist das Ändern privater Eigenschaften in exportierten Klassen vom Tisch. Nur geschützte und öffentliche Objekte können geändert werden. Jetzt interessieren mich die Java-Interna-Exporte nicht mehr so ​​sehr, sondern eher Bibliotheken von Drittanbietern, bei denen ich manchmal Zugriff auf eine private Variable benötige, um einen bestimmten Wert festzulegen. Das wäre in diesem Schema nicht mehr möglich, wenn es sich als Modul definieren würde. Ist das richtig?
Tschallacka
1
Ich habe keine direkte Erfahrung damit, aber das wäre mein Verständnis und würde neben dem an anderer Stelle erwähnten Artikel ( jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html ) lesen, der zu sein scheint der Fall. Starten Sie Ihre JVM mit –illegal-access=permit...
ptomli
1
Nun, das wird die Dinge interessanter machen, wenn versucht wird, die Dinge für einige Dinge zum Laufen zu bringen, wenn sie sich entscheiden, zum Modul zu gehen. Super lustige Zeiten voraus.
Tschallacka
1
Für verschiedene Werte vonfun
ptomli
Ich habe die andere Antwort akzeptiert, weil sie eine ausführlichere Erklärung lieferte und eher eine Antwort auf die Frage war, aber leider kann ich zwei Antworten nicht akzeptieren.
Tschallacka
13

Schauen Sie sich einfach die setAccessible()Methode an, mit der auf privateFelder und Methoden zugegriffen wird:

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-

Jetzt sind viel mehr Bedingungen erforderlich, damit diese Methode funktioniert. Der einzige Grund, warum fast die gesamte ältere Software nicht beschädigt wird, besteht darin, dass Module, die automatisch aus einfachen JARs generiert werden, sehr zulässig sind (alles für alle öffnen und exportieren).

user158037
quelle
1

Wenn Sie die Option zum Hinzufügen und Öffnen verwenden möchten, finden Sie hier einen Befehl, um herauszufinden, welches Modul welches Paket bereitstellt ->

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

Der Name des Moduls wird mit dem @ angezeigt, während der Name der Pakete ohne das @ angezeigt wird

HINWEIS: getestet mit JDK 11

WICHTIG: Offensichtlich ist besser als der Anbieter des Pakets den illegalen Zugriff nicht macht

Carlos Saltos
quelle