Wie löst man InaccessibleObjectException (“Unable to make {member} accessable: module {A} does not ‘opens {package}’ to {B}”) unter Java 9?

Lesezeit: 9 Minuten

Wie lost man InaccessibleObjectException Unable to make member accessable module
Nicolai Parlog

Diese Ausnahme tritt in einer Vielzahl von Szenarien auf, wenn eine Anwendung auf Java 9 ausgeführt wird. Bestimmte Bibliotheken und Frameworks (Spring, Hibernate, JAXB) sind besonders anfällig dafür. Hier ist ein Beispiel von Javassist:

java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @1941a8ff
    at java.base/jdk.internal.reflect.Reflection.throwInaccessibleObjectException(Reflection.java:427)
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:201)
    at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:192)
    at java.base/java.lang.reflect.Method.setAccessible(Method.java:186)
    at javassist.util.proxy.SecurityActions.setAccessible(SecurityActions.java:102)
    at javassist.util.proxy.FactoryHelper.toClass2(FactoryHelper.java:180)
    at javassist.util.proxy.FactoryHelper.toClass(FactoryHelper.java:163)
    at javassist.util.proxy.ProxyFactory.createClass3(ProxyFactory.java:501)
    at javassist.util.proxy.ProxyFactory.createClass2(ProxyFactory.java:486)
    at javassist.util.proxy.ProxyFactory.createClass1(ProxyFactory.java:422)
    at javassist.util.proxy.ProxyFactory.createClass(ProxyFactory.java:394)

Die Nachricht sagt:

Geschütztes Finale kann nicht erstellt werden java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) löst java.lang.ClassFormatError access aus: Modul java.base “öffnet java.lang” nicht für unbenanntes Modul @1941a8ff

Was kann getan werden, um die Ausnahme zu vermeiden und das Programm erfolgreich auszuführen?

Wie lost man InaccessibleObjectException Unable to make member accessable module
Nicolai Parlog

Die Ausnahme wird durch die verursacht Java-Plattformmodulsystem das in Java 9 eingeführt wurde, insbesondere die Implementierung einer starken Kapselung. Es erlaubt nur betreten Unter bestimmten Bedingungen sind die wichtigsten:

  • der Typ muss öffentlich sein
  • das besitzende Paket muss exportiert werden

Die gleichen Einschränkungen gelten für die Reflektion, die der die Ausnahme verursachende Code zu verwenden versuchte. Genauer gesagt wird die Ausnahme durch einen Aufruf von verursacht setAccessible. Dies ist im Stack-Trace oben zu sehen, wo die entsprechenden Zeilen drin sind javassist.util.proxy.SecurityActions wie folgt aussehen:

static void setAccessible(final AccessibleObject ao,
                          final boolean accessible) {
    if (System.getSecurityManager() == null)
        ao.setAccessible(accessible); // <~ Dragons
    else {
        AccessController.doPrivileged(new PrivilegedAction() {
            public Object run() {
                ao.setAccessible(accessible);  // <~ moar Dragons
                return null;
            }
        });
    }
}

Um sicherzustellen, dass das Programm erfolgreich läuft, muss das Modulsystem davon überzeugt werden, den Zugriff auf das Element zu erlauben, auf dem es sich befindet setAccessible hieß. Alle dafür erforderlichen Informationen sind in der Ausnahmemeldung enthalten, aber es gibt sie eine Reihe von Mechanismen um das zu erreichen. Welches das beste ist, hängt von dem genauen Szenario ab, das es verursacht hat.

{member} kann nicht zugänglich gemacht werden: Modul {A} ‘öffnet {Paket}’ nicht für {B}

Die bei weitem prominentesten Szenarien sind die folgenden beiden:

  1. Eine Bibliothek oder ein Framework verwendet Reflektion, um ein JDK-Modul aufzurufen. In diesem Szenario:

    • {A} ist ein Java-Modul (mit dem Präfix java. oder jdk.)
    • {member} und {package} sind Teile der Java-API
    • {B} ist eine Bibliothek, ein Framework oder ein Anwendungsmodul; häufig unnamed module @...
  2. Eine reflexionsbasierte Bibliothek/Framework wie Spring, Hibernate, JAXB, … spiegelt den Anwendungscode wider, um auf Beans, Entities, … zuzugreifen. In diesem Szenario:

    • {A} ist ein Anwendungsmodul
    • {member} und {package} sind Teil des Anwendungscodes
    • {B} ist entweder ein Rahmenmodul oder unnamed module @...

Beachten Sie, dass einige Bibliotheken (z. B. JAXB) bei beiden Konten fehlschlagen können. Sehen Sie sich also genau an, in welchem ​​​​Szenario Sie sich befinden! Der fragliche Fall ist Fall 1.

1. Reflektierender Aufruf in JDK

Die JDK-Module sind für Anwendungsentwickler unveränderlich, sodass wir ihre Eigenschaften nicht ändern können. Damit bleibt nur eine mögliche Lösung: Befehlszeilen-Flags. Mit ihnen ist es möglich, konkrete Pakete zum Nachdenken zu öffnen.

Also in einem Fall wie oben (gekürzt) …

java.lang.ClassLoader.defineClass kann nicht zugänglich gemacht werden: Modul java.base “öffnet java.lang” nicht für unbenanntes Modul @1941a8ff

… die richtige Lösung besteht darin, die JVM wie folgt zu starten:

# --add-opens has the following syntax: {A}/{package}={B}
java --add-opens java.base/java.lang=ALL-UNNAMED

Wenn sich der reflektierende Code in einem benannten Modul befindet, ALL-UNNAMED kann durch seinen Namen ersetzt werden.

Beachten Sie, dass es manchmal schwierig sein kann, einen Weg zu finden, dieses Flag auf die JVM anzuwenden, die den reflektierenden Code tatsächlich ausführt. Dies kann besonders schwierig sein, wenn der betreffende Code Teil des Build-Prozesses des Projekts ist und in einer JVM ausgeführt wird, die das Build-Tool hervorgebracht hat.

Wenn zu viele Flags hinzugefügt werden müssen, sollten Sie die Verwendung von in Erwägung ziehen Kapselungs-Kill-Switch --permit-illegal-access stattdessen. Dadurch kann der gesamte Code im Klassenpfad die insgesamt benannten Module widerspiegeln. Beachten Sie, dass dieses Flag funktioniert nur in Java 9!

2. Reflexion über den Anwendungscode

In diesem Szenario ist es wahrscheinlich, dass Sie das Modul bearbeiten können, in das die Reflektion einbricht. (Wenn nicht, sind Sie effektiv in Fall 1.) Das bedeutet, dass Befehlszeilen-Flags nicht erforderlich sind und stattdessen Module {A}Der Deskriptor von kann verwendet werden, um seine Interna zu öffnen. Es gibt eine Vielzahl von Auswahlmöglichkeiten:

  • Exportieren Sie das Paket mit exports {package}wodurch es zur Kompilier- und Laufzeit für den gesamten Code verfügbar ist
  • Exportieren Sie das Paket mit in das zugreifende Modul exports {package} to {B}wodurch es zur Kompilierungs- und Laufzeit verfügbar ist, aber nur für {B}
  • Öffnen Sie das Paket mit opens {package}wodurch es zur Laufzeit (mit oder ohne Reflektion) für den gesamten Code verfügbar ist
  • Öffnen Sie das Paket zum zugreifenden Modul mit opens {package} to {B}wodurch es zur Laufzeit (mit oder ohne Reflektion) verfügbar ist, aber nur um {B}
  • Öffnen Sie das gesamte Modul mit open module {A} { ... }das alle seine Pakete zur Laufzeit (mit oder ohne Reflektion) für den gesamten Code verfügbar macht

Sehen dieser Beitrag für eine detailliertere Diskussion und einen Vergleich dieser Ansätze.

  • Ist Lombok in Fall 1 und es ist schwer, einen Weg zu finden, dieses Flag auf die JVM anzuwenden, die den reflektierenden Code tatsächlich ausführt, da er Teil des Build-Prozesses des Projekts ist?

    – zhuguowei

    8. Januar 2017 um 4:03 Uhr


  • Ja, Lombok ist Fall 1. Die Frage, wie schwer es ist, die Flaggen anzubringen, gehört hierher.

    – Nicolai Parlog

    8. Januar 2017 um 9:07 Uhr

  • Danke für die Erklärung. Zum mein sehr ähnlicher Fall es scheitert immer noch sogar mit das --add-opens Möglichkeit. Seltsam.

    – Karußell

    22. Januar 2017 um 0:16 Uhr

  • Auf den ersten Blick könnte das Problem darin bestehen, dass Sie das Surefire-Plugin konfigurieren, aber der Fehler dadurch verursacht wird das Failsafe-Plugin. Wenn ich falsch liege, stellen Sie bitte eine separate Frage.

    – Nicolai Parlog

    22. Januar 2017 um 18:28 Uhr

  • @Nicolai Ich denke, Sie sollten Ihre Antwort aktualisieren, da JDK 9 jetzt standardmäßig illegalen Zugriff erlaubt und --permit-illegal-access wird sich ändern zu --illegal-access: mail.openjdk.java.net/pipermail/jigsaw-dev/2017-May/012673.html

    – ZhekaKozlov

    30. Mai 2017 um 4:45 Uhr


Für Eclipse müssen Sie zwei Dinge tun: Gehen Sie zu

  1. Fenster -> Einstellungen -> Java -> Compiler
    Set Compiler Compliance Level to Specific version In meinem Fall war die Eclipse-Version auf JDK 16 eingestellt. Ich habe sie auf 1.8 zurückgesetzt, da mein Code in 1.8 geschrieben wurde

  2. Fenster -> Einstellungen -> Java -> Installierte JREs.
    Fügen Sie den JRE-Installationspfad hinzu (Wählen Sie Standard-VM aus)

Bei mir hat es problemlos funktioniert..

  • Dies löste mein Problem wie Charme!

    – Bhavin Shah

    8. Oktober 2021 um 15:29 Uhr

  • schnelle Lösung!

    – Sritam Jagadev

    9. Februar um 17:08 Uhr

Nur ein aktuelles Feedback

Viele Vorschläge zur Lösung dieses Problems haben mit der Option vm launcher zu tun --illegal-access.

Laut Oracle mit JEP 403 (link1) und JEP 403 (link2) wofür entschieden wurde wird ab JDK 17 ausgeliefert die Launcher-Option --illegal-access wird aufhören zu arbeiten!

Zusammenfassung Kapseln Sie alle internen Elemente des JDK streng, mit Ausnahme kritischer interner APIs wie sun.misc.Unsafe. Es wird nicht mehr möglich sein, die starke Kapselung interner Elemente über eine einzige Befehlszeilenoption zu lockern, wie dies in JDK 9 bis JDK 16 möglich war.

Und

Mit dieser Änderung ist es Endbenutzern wird es nicht mehr möglich sein, die Option –illegal-access zu verwenden um den Zugriff auf interne Elemente des JDK zu ermöglichen. (Eine Liste der betroffenen Pakete ist hier verfügbar.) Die Pakete sun.misc und sun.reflect werden weiterhin vom jdk.unsupported-Modul exportiert und sind weiterhin geöffnet, sodass Code über Reflektion auf ihre nicht öffentlichen Elemente zugreifen kann. Auf diese Weise werden keine anderen JDK-Pakete geöffnet.

Die Nutzung ist weiterhin möglich –add-opens
Befehlszeilenoption oder das Manifestattribut Add-Opens JAR-Datei, um bestimmte Pakete zu öffnen.

Die folgende Lösung funktioniert also weiterhin

# --add-opens has the following syntax: {A}/{package}={B}
java --add-opens java.base/java.lang=ALL-UNNAMED

Aber die Lösung mit --illegal-access wird aufhören zu arbeiten JDK 17 und weiter.

Die Verwendung von –add-opens sollte als Problemumgehung betrachtet werden. Das Richtige ist für Spring, Hibernate und andere Bibliotheken, die illegalen Zugriff nutzen, um ihre Probleme zu beheben.

Dies ist ein sehr herausforderndes Problem zu lösen; und wie von anderen angemerkt, ist die Option –add-opens nur eine Problemumgehung. Die Dringlichkeit, die zugrunde liegenden Probleme zu lösen, wird erst zunehmen, wenn Java 9 öffentlich verfügbar wird.

Ich habe mich auf dieser Seite wiedergefunden, nachdem ich beim Testen meiner Hibernate-basierten Anwendung auf Java 9 genau diesen Javassist-Fehler erhalten hatte. Und da ich Java 7, 8 und 9 auf mehreren Plattformen unterstützen möchte, hatte ich Mühe, die beste Lösung zu finden. (Beachten Sie, dass Java 7- und 8-JVMs sofort abbrechen, wenn sie ein nicht erkanntes „–add-opens“-Argument in der Befehlszeile sehen; daher kann dies nicht mit statischen Änderungen an Batchdateien, Skripten oder Verknüpfungen gelöst werden.)

Es wäre schön, offizielle Anleitungen von den Autoren der Mainstream-Bibliotheken (wie Spring und Hibernate) zu erhalten, aber 100 Tage vor der derzeit geplanten Veröffentlichung von Java 9 scheint dieser Rat immer noch schwer zu finden.

Nach vielen Experimenten und Tests war ich erleichtert, eine Lösung für Hibernate zu finden:

  1. Verwenden Sie Hibernate 5.0.0 oder höher (frühere Versionen funktionieren nicht) und
  2. Anfrage Build-Time-Bytecode-Verbesserung (unter Verwendung der Gradle-, Maven- oder Ant-Plug-ins).

Dies vermeidet die Notwendigkeit, dass Hibernate Javassist-basierte Klassenänderungen zur Laufzeit durchführt, wodurch der im ursprünglichen Post gezeigte Stack-Trace eliminiert wird.

ABER, sollten Sie Ihre Anwendung anschließend gründlich testen. Die von Hibernate zur Erstellungszeit angewendeten Bytecode-Änderungen scheinen sich von den zur Laufzeit angewendeten zu unterscheiden, was zu einem leicht unterschiedlichen Anwendungsverhalten führt. Komponententests in meiner App, die jahrelang erfolgreich waren, schlugen plötzlich fehl, als ich die Build-Time-Bytecode-Erweiterung aktivierte. (Ich musste neuen LazyInitializationExceptions und anderen Problemen nachjagen.) Und das Verhalten scheint von einer Version von Hibernate zur anderen zu variieren. Mit Vorsicht fortfahren.

Wie lost man InaccessibleObjectException Unable to make member accessable module
Rituparna Bhattacharyya

Fügen Sie –illegal-access=warn und –add-opens java.base/java.lang=ALL-UNNAMED zu Ihrer eclipse.ini hinzu

Wie lost man InaccessibleObjectException Unable to make member accessable module
Dhaval Patel

Ich hatte das gleiche Problem im Jahr 2021 bei der Verwendung von openJDK 1.8 und STS 4.

Window => Preferences => Java => Installed JREs.

Ich habe eine neue JRE (unten erwähnt) mit der Option „Hinzufügen“ hinzugefügt, zum Ordner „openJdk“ navigiert und „OK“ ausgewählt. Legen Sie das neue JDK als Standard fest. Klicken Sie auf Übernehmen und schließen.

/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre

Es funktionierte wie am Schnürchen 🙂

915120cookie-checkWie löst man InaccessibleObjectException (“Unable to make {member} accessable: module {A} does not ‘opens {package}’ to {B}”) unter Java 9?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy