Что такое нелегальный рефлексивный доступ
Вокруг нелегального рефлексивного доступа в Java 9 много вопросов.
Теперь то, что я не могу найти, потому что все, что Google изрыгает, это люди, пытающиеся обойти сообщения об ошибках, это то, чем на самом деле является нелегальный рефлексивный доступ.
Итак, мой вопрос довольно прост:
Что определяет незаконный рефлексивный доступ и какие обстоятельства вызывают предупреждение?
Я понял, что это как-то связано с принципами инкапсуляции, которые были введены в Java 9, но с тем, как все это сочетается и что вызывает предупреждение, в каком сценарии я не могу найти объяснения.
6 ответов
Помимо понимания доступа между модулями и их соответствующими пакетами. Я полагаю, что суть этого заключается в модульной системе #Relaxed-strong-encapsulation, и я бы просто выбрал соответствующие ее части, чтобы попытаться ответить на вопрос.
Что определяет незаконный рефлексивный доступ и какие обстоятельства вызывают предупреждение?
Для облегчения перехода на Java9 сильная инкапсуляция модулей может быть ослаблена.
Реализация может предоставлять статический доступ, то есть посредством скомпилированного байт-кода.
Может предоставить средство для вызова своей системы времени выполнения с одним или несколькими пакетами одного или нескольких из его модулей, открытых для кода во всех безымянных модулях, то есть для кодирования на пути к классам. Если система во время выполнения вызывается таким образом, и если при этом некоторые вызовы 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
Последнее и важное замечание, пытаясь гарантировать, что вы не столкнетесь с такими предупреждениями и будете в безопасности в будущем, все, что вам нужно сделать, - это убедиться, что ваши модули не делают эти незаконные отражающие доступы.:)
Я нашел статью Oracle о модульной системе Java 9
По умолчанию тип модуля не доступен для других модулей, если он не является общедоступным типом и вы не экспортируете его пакет. Вы выставляете только те пакеты, которые хотите выставить. С Java 9 это также относится к рефлексии.
Как указано в /questions/35481567/chto-takoe-nelegalnyij-refleksivnyij-dostup/35481573#35481573, различия между AccessibleObject#setAccessible
для JDK8 и JDK9 поучительны. В частности, добавлен JDK9
Этот метод может использоваться вызывающей стороной в классе C, чтобы разрешить доступ к члену объявления класса D, если выполнено любое из следующих действий:
- C и D находятся в одном модуле.
- Элемент public, а D public в пакете, который модуль, содержащий D, экспортирует, по крайней мере, в модуль, содержащий C.
- Элемент защищен статически, D является открытым в пакете, который модуль, содержащий D, экспортирует, по крайней мере, в модуль, содержащий C, а C является подклассом D.
- D находится в пакете, который модуль, содержащий D, открывает по крайней мере для модуля, содержащего C. Все пакеты в неназванных и открытых модулях открыты для всех модулей, и поэтому этот метод всегда успешно выполняется, когда D находится в неназванном или открытом модуле.
который подчеркивает важность модулей и их экспорта (в Java 9)
Просто посмотри на setAccessible()
метод, используемый для доступа private
поля и методы:
https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AccessibleObject.html
https://docs.oracle.com/javase/9/docs/api/java/lang/reflect/AccessibleObject.html
Теперь для работы этого метода требуется намного больше условий. Единственная причина, по которой он не нарушает почти все старое программное обеспечение, заключается в том, что модули, автоматически сгенерированные из простых JAR-файлов, очень разрешительны (открывают и экспортируют все для всех).
если вы хотите скрыть предупреждения, вы можете просто использовать опцию «--add-opens»
--add-opens <source-module>/<package>=<target-module>(,<target-module>)*
например у вас есть ошибка:
java.lang.ClassLoader.findLoadedClass(java.lang.String)
Сначала откройте документацию String java 11 Class String, где вы можете найти имя модуля и пакета
Модуль java.base, Пакет java.lang
Решение:
java --add-opens=java.base/java.lang=ALL-UNNAMED -jar example.jar
Если вы хотите использовать опцию add-open, вот команда, чтобы узнать, какой модуль предоставляет какой пакет ->
java --list-modules | tr @ " " | awk '{ print $1 }' | xargs -n1 java -d
имя модуля будет отображаться с @, а имя пакетов без него
ПРИМЕЧАНИЕ: протестировано с JDK 11
ВАЖНО: очевидно, что лучше, чем провайдер пакета не делает незаконный доступ
ПРЕДУПРЕЖДЕНИЕ: Произошла недопустимая операция отражающего доступа ПРЕДУПРЕЖДЕНИЕ: Незаконный отражающий доступ для org.springframework.cglib.core.ReflectUtils (файл: / C:/Users/91844/.m2/repository/org/springframework/spring-core/5.3.3 / spring-core-5.3.3.jar) в метод java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) ПРЕДУПРЕЖДЕНИЕ. Сообщите об этом в Сопровождающие org.springframework.cglib.core.ReflectUtils ПРЕДУПРЕЖДЕНИЕ: используйте --illegal-access=warn, чтобы включить предупреждения о дальнейших незаконных операциях отражающего доступа. ПРЕДУПРЕЖДЕНИЕ: Все операции незаконного доступа будут запрещены в будущих версиях.