Вопрос-ответ

What is an illegal reflective access?

Что такое незаконный отражающий доступ?

Существует много вопросов о незаконном отражающем доступе в Java 9.

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

Итак, мой вопрос таков:

Что определяет незаконный отражающий доступ и какие обстоятельства вызывают предупреждение?

Я понял, что это как-то связано с принципами инкапсуляции, которые были представлены в Java 9, но я не могу найти объяснения того, как все это сочетается, что вызывает предупреждение и в каком сценарии.

Переведено автоматически
Ответ 1

Помимо понимания доступа к модулям и их соответствующим пакетам. Я считаю, что суть этого заключается в модульной системе #Relaxed-strong-encapsulation, и я бы просто выбрал соответствующие ее части, чтобы попытаться ответить на вопрос.


Что определяет незаконный отражающий доступ и какие обстоятельства вызывают предупреждение?


Чтобы облегчить переход на Java-9, строгую инкапсуляцию модулей можно было бы ослабить.


  • Реализация может предоставлять статический доступ, то есть посредством скомпилированного байт-кода.



  • Может предоставлять средства для вызова своей системы во время выполнения с одним или несколькими пакетами одного или нескольких ее модулей, открытыми для кода во всех неназванных модулях, т. е. для кода в пути к классу. Если система времени выполнения вызывается таким образом, и если при этом некоторые вызовы API отражения завершаются успешно, тогда как в противном случае они завершились бы неудачей.



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


Как все это сочетается и что вызывает предупреждение, в каком сценарии?


Это ослабление инкапсуляции контролируется во время выполнения новой опцией запуска, --illegal-access которая по умолчанию в Java9 равна permit. Режим permit обеспечивает


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


Режимы настраиваются с помощью значений debug(сообщение, а также stacktrace для каждого такого доступа), warn(сообщение для каждого такого доступа) и deny(отключает такие операции).


Несколько вещей, которые необходимо отладить и исправить в приложениях::-


  • Запустите его с помощью --illegal-access=deny, чтобы узнать об этом и избегать открытияпакетов из одного модуля в другой без объявления модуля, включающего такую директиву (opens) или явного использования --add-opens аргумента виртуальной машины.

  • Статические ссылки из скомпилированного кода на JDK-внутренние API могут быть идентифицированы с помощью jdeps инструмента с --jdk-internals опцией


Предупреждающее сообщение, выдаваемое при обнаружении незаконной операции отражающего доступа, имеет следующий вид:


WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM


где:


$PERPETRATOR полное имя типа, содержащего код, который вызвал рассматриваемую отражающую операцию, плюс источник кода (т. Е. Путь к JAR-файлу), если таковой имеется, и


$VICTIM это строка, описывающая элемент, к которому осуществляется доступ, включая полное имя заключающего типа


Вопросы для такого примера предупреждения: = JDK9: Произошла незаконная операция отражающего доступа. org.python.core.PySystemState

Последнее и важное замечание: пытаясь гарантировать, что вы не столкнетесь с подобными предупреждениями и будете в безопасности в будущем, все, что вам нужно сделать, это убедиться, что ваши модули не выполняют эти незаконные отражающие обращения. :)

Ответ 2

Я нашел статью Oracle о модульной системе Java 9


По умолчанию тип в модуле недоступен для других модулей, если только это не открытый тип и вы не экспортируете его пакет. Вы предоставляете только те пакеты, которые хотите предоставить. В Java 9 это также относится к отражению.


Как указано в https://javalang.ru/a/50251958/134894, различия между AccessibleObject#setAccessible для JDK8 и JDK9 поучительны. В частности, JDK9 добавил


Этот метод может использоваться вызывающим объектом в классе C, чтобы разрешить доступ к члену объявляющего класса D, если выполняется любое из следующих действий:



  • C и D находятся в одном модуле.

  • Элемент является общедоступным, а D является общедоступным в пакете, который модуль, содержащий D, экспортирует по крайней мере в модуль, содержащий C.

  • Элемент защищен статически, D является общедоступным в пакете, который модуль, содержащий D, экспортирует по крайней мере в модуль, содержащий C, а C является подклассом D.

  • D находится в пакете, который модуль, содержащий D, открывает по крайней мере для модуля, содержащего C. Все пакеты в неназванных и открытых модулях открыты для всех модулей, и поэтому этот метод всегда срабатывает, когда D находится в неназванном или открытом модуле.


в котором подчеркивается важность модулей и их экспорта (в Java 9)

Ответ 3

если вы хотите скрыть предупреждения, вы могли бы просто использовать опцию "--add-opens"

--add-opens <source-module>/<package>=<target-module>(,<target-module>)*

например, у вас ошибка:

java.lang.ClassLoader.findLoadedClass(java.lang.String)

Первая открытая строка документации java 11
Строка класса, где вы можете найти название модуля и пакета

Модуль java.base, пакет java.lang

Решение:

java --add-opens=java.base/java.lang=ALL-UNNAMED -jar example.jar
Ответ 4

Если вы хотите выбрать опцию добавить-открыть, вот команда, чтобы найти, какой модуль предоставляет какой пакет ->

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

имя модуля будет отображаться с символом @, а имя пакетов - без него

ПРИМЕЧАНИЕ: протестировано с помощью JDK 11

ВАЖНО: очевидно, что лучше, чем поставщик пакета не выполняет незаконный доступ

java