Как решить InaccessibleObjectException ("невозможно сделать {member} доступным: модуль {A} не" открывает {package} " в {B}") на Java 9?

это исключение возникает в самых разных сценариях при запуске приложения на Java 9. Некоторые библиотеки и фреймворки (Spring, Hibernate, JAXB) особенно подвержены этому. Вот пример из 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)

в сообщении говорится:

невозможно сделать защищенный окончательный java.ленг.Класс java.ленг.загрузчик классов.defineClass (java.ленг.String, byte [], int,int, java.безопасность.ProtectionDomain) бросает java.ленг.ClassFormatError доступно: модуль Ява.база не " открывает java.lang " в неназванный модуль @1941a8ff

что можно сделать, чтобы избежать исключения и успешно запустить программу?

4 ответов


исключение, вызванное Система Модулей Платформы Java это было введено в Java 9, особенно его реализация сильной инкапсуляции. Это только позволяет открыть при определенных условиях, наиболее крупными из них являются:

  • тип должен быть публичным
  • пакет-владелец должен быть экспортирован

те же ограничения верны для отражения, которое код, вызывающий исключение, попытался использовать. Точнее исключение вызвано вызовом setAccessible. Это можно увидеть в трассировке стека выше, где соответствующие строки в javassist.util.proxy.SecurityActions выглядеть следующим образом:

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;
            }
        });
    }
}

чтобы убедиться, что программа работает успешно, модульная система должна быть убеждена в том, что доступ к элементу, на котором setAccessible называлась. Вся информация, необходимая для этого, содержится в сообщении об исключении, но есть ряд механизмов для достижения этот. Какой из них лучший, зависит от точного сценария, который его вызвал.

невозможно сделать {member} доступным: модуль {A} не "открывает {package}" для {B}

на сегодняшний день наиболее заметными сценариями являются следующие два:

  1. библиотека или платформа использует отражение для вызова модуля JDK. В этом сценарии:

    • {A} - это модуль Java (с префиксом java. или jdk.)
    • {member} и {package} являются частью API Java
    • {B} - это библиотека, фреймворк или модуль приложения; часто unnamed module @...
  2. библиотека/фреймворк на основе отражения, такие как Spring, Hibernate, JAXB, ... отражает код приложения для доступа к бобам, сущностям,.... В этом сценарии:

    • {A} является модулем приложения
    • {member} и {package} часть код приложения
    • {B} является либо модулем фреймворка, либо unnamed module @...

обратите внимание, что некоторые библиотеки (например, JAXB) могут терпеть неудачу на обеих учетных записях, поэтому внимательно посмотрите, в каком сценарии вы находитесь! В вопрос случае 1.

1. Отражающий вызов в JDK

модули JDK неизменяемы для разработчиков приложений, поэтому мы не можем изменить их свойства. Остается только одно возможное решение: флаги командной строки. С их помощью можно открывать определенные пакеты для размышления.

так в случае, как указано выше (сокращено)...

невозможно сделать java.ленг.загрузчик классов.defineClass доступный: модуль java.база не " открывает java.lang " в неназванный модуль @1941a8ff

... правильное исправление-запустить JVM следующим образом:

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

если отражающий код находится в именованном модуле,ALL-UNNAMED может быть заменено его именем.

обратите внимание, что иногда бывает трудно найти способ применить этот флаг к JVM, который фактически выполнит отражающий код. Это может быть особенно сложно, если рассматриваемый код является частью процесса сборки проекта и выполняется в JVM, который породил инструмент сборки.

если слишком много флагов для добавления, вы можете использовать инкапсуляция выключатель --permit-illegal-access вместо. Это позволит весь код на пути к классу, чтобы отразить все названные модули. Обратите внимание, что этот флаг будет работать только в Java 9!

2. Отражение Кода Приложения

в этом случае вполне вероятно, что вы можете редактировать модуль, отражение используется для взлома. (Если нет, вы эффективно в случае 1.) Это означает, что флаги командной строки не нужны и вместо модуля {A}'s дескриптор может быть использован для открытия его внутренних. Существует множество выбор:

  • экспорт пакета с exports {package}, что делает его доступным во время компиляции и выполнения для всего кода
  • экспорт пакета в модуль доступа с помощью exports {package} to {B}, что делает его доступным во время компиляции и выполнения, но только для {B}
  • открыть пакет с opens {package}, что делает его доступным во время выполнения (С или без отражения) для всего кода
  • откройте пакет для модуля доступа с помощью opens {package} to {B}, что делает она доступна во время выполнения (С или без отражения), но только в {B}
  • откройте весь модуль с помощью open module {A} { ... }, который делает все свои пакеты доступными во время выполнения (С или без отражения) для всего кода

посмотреть этот пост для более детального обсуждения и сравнения этих подходов.


использование --add-opens следует рассматривать как обходной путь. Правильно для Spring, Hibernate и других библиотек делать незаконный доступ, чтобы исправить свои проблемы.


Это очень сложная проблема для решения; и, как отметили другие, параметр --add-opens является только обходным путем. Срочность решения базовых проблем будет расти только после того, как Java 9 станет общедоступной.

Я оказался на этой странице после получения этой точной ошибки Javassist при тестировании моего приложения на основе гибернации на Java 9. И поскольку я стремлюсь поддержки Java 7, 8 и 9 на нескольких платформах, я изо всех сил пытался найти лучшее решение. (Обратите внимание, что Java 7 и 8 JVMs немедленно прекратит работу, когда они увидят непризнанный аргумент "--add-opens " в командной строке; поэтому это не может быть решено со статическими изменениями в пакетных файлах, сценариях или ярлыках.)

было бы неплохо получить официальное руководство от авторов основных библиотек (таких как Spring и Hibernate), но с 100 днями до текущего прогнозируемого выпуска Java 9, Этот совет все еще кажется трудным найти.

после долгих экспериментов и испытаний я был облегченно найти решение для Hibernate:

  1. использовать Hibernate 5.0.0 или выше (более ранние версии не будут работать), и
  2. запрос улучшение байт-кода времени сборки (используя плагины Gradle, Maven или Ant).

Это позволяет избежать необходимости Hibernate выполнять изменения класса на основе Javassist во время выполнения, устраняя трассировку стека, показанную в исходном сообщении.

, вы должны тщательно Проверьте свое приложение позже. Изменения байт-кода, применяемые Hibernate во время сборки, отличаются от изменений, применяемых во время выполнения, что вызывает несколько иное поведение приложения. Модульные тесты в моем приложении, которые преуспели в течение многих лет, внезапно потерпели неудачу, когда я включил улучшение байт-кода во время сборки. (Мне пришлось искать новые LazyInitializationExceptions и другие проблемы.) И поведение, похоже, варьируется от одной версии Hibernate к другой. Действовать осторожно.


у меня были предупреждения с hibernate 5.

Illegal reflective access by javassist.util.proxy.SecurityActions

я добавил последнюю библиотеку javassist в зависимости gradle:

compile group: 'org.javassist', name: 'javassist', version: '3.22.0-GA'

это решило мою проблему.