Как решить 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}
на сегодняшний день наиболее заметными сценариями являются следующие два:
-
библиотека или платформа использует отражение для вызова модуля JDK. В этом сценарии:
-
{A}
- это модуль Java (с префиксомjava.
илиjdk.
) -
{member}
и{package}
являются частью API Java -
{B}
- это библиотека, фреймворк или модуль приложения; частоunnamed module @...
-
-
библиотека/фреймворк на основе отражения, такие как 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:
- использовать Hibernate 5.0.0 или выше (более ранние версии не будут работать), и
- запрос улучшение байт-кода времени сборки (используя плагины 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'
это решило мою проблему.