Что такое незаконный отражающий доступ?
Существует много вопросов о незаконном отражающем доступе в 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
ВАЖНО: очевидно, что лучше, чем поставщик пакета не выполняет незаконный доступ