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

128

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

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

Итак, мой вопрос довольно прост:

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

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

Tschallacka
источник
2
это тоже может вас заинтересовать: jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html
Эдвин,

Ответы:

56

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

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

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

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

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

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

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

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

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

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


Вот несколько вещей, которые нужно отлаживать и исправлять в приложениях:

  • Запустите его с, --illegal-access=denyчтобы узнать и избежать открытия пакетов от одного модуля к другому без объявления модуля, включая такую ​​директиву ( opens) или явного использования --add-opensаргумента VM arg.
  • Статические ссылки из скомпилированного кода на внутренние API JDK могут быть идентифицированы с помощью jdepsинструмента с --jdk-internalsопцией

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

WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM

где:

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

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

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

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

Naman
источник
23

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

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

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

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

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

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

ptomli
источник
2
Поэтому, если я прочитал эту статью, о правильном изменении частных свойств в экспортированных классах не будет. Изменять можно только защищенные и общедоступные свойства. Теперь меня не волнует внутренний экспорт java, а больше о сторонних библиотеках, где мне иногда нужен доступ к частной переменной, для которой нужно установить определенное значение. Это было бы невозможно в этой схеме, если бы она определяла себя как модуль, верно?
Tschallacka
1
У меня нет непосредственного опыта в этом, но это было бы моим пониманием, и я прочитал вместе со статьей, упомянутой в другом месте ( jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html ), которая, казалось бы, дело. Запустите свою JVM с –illegal-access=permit...
ptomli
1
Что ж, это сделает вещи более интересными, пытаясь заставить что-то работать для некоторых вещей, когда они решат перейти на модульный путь. Впереди супер веселые времена.
Tschallacka
1
Для разных значенийfun
ptomli 09
Я принял другой ответ, потому что он дал больше объяснений и был скорее ответом на вопрос, но, к сожалению, я не могу принять два ответа.
Tschallacka
13

Просто посмотрите на setAccessible()метод, используемый для доступа к privateполям и методам:

https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-

https://docs.oracle.com/javase/9/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-

Теперь для работы этого метода требуется гораздо больше условий. Единственная причина, по которой он не ломает почти все старое программное обеспечение, заключается в том, что модули, автоматически созданные из простых JAR-файлов, очень разрешительны (открывать и экспортировать все для всех).

user158037
источник
1

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

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

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

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

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

Карлос Сальтос
источник