Как скрыть предупреждение "незаконный отражающий доступ" в java 9 без аргумента JVM?
Я просто попытался запустить свой сервер с Java 9 и получил следующее предупреждение:
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by io.netty.util.internal.ReflectionUtil (file:/home/azureuser/server-0.28.0-SNAPSHOT.jar) to constructor java.nio.DirectByteBuffer(long,int)
WARNING: Please consider reporting this to the maintainers of io.netty.util.internal.ReflectionUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Я хотел бы скрыть это предупреждение без добавления --illegal-access=deny
для параметров JVM во время запуска. Что-то вроде:
System.setProperty("illegal-access", "deny");
есть ли способ сделать это?
все связанные ответы, предлагающие использовать параметры JVM, я хотел бы отключить это от кода. Это возможно?
чтобы уточнить-мой вопрос заключается в том, чтобы превратить это предупреждение из кода, а не через JVM аргументы / флаги, как указано в аналогичных вопросах.
4 ответов
есть способы отключить предупреждение о незаконном доступе, хотя я не рекомендую делать это.
1. Простой подход
С предупреждением, печатается в поток ошибок по умолчанию, вы можете просто закрыть этот поток и перенаправить stderr
to stdout
.
public static void disableWarning() {
System.err.close();
System.setErr(System.out);
}
Примечания:
- этот подход объединяет потоки ошибок и выходных данных. В некоторых случаях это может быть нежелательно.
- вы не можете перенаправить предупреждающее сообщение, просто позвонив
System.setErr
, так как ссылка на поток ошибок сохраняется вIllegalAccessLogger.warningStream
поле в начале начальной загрузки JVM.
2. Сложный подход без изменения stderr
хорошая новость в том, что sun.misc.Unsafe
по-прежнему доступен в JDK 9 без предупреждений. Раствор для сброса внутреннего IllegalAccessLogger
С помощью небезопасного API.
public static void disableWarning() {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe u = (Unsafe) theUnsafe.get(null);
Class cls = Class.forName("jdk.internal.module.IllegalAccessLogger");
Field logger = cls.getDeclaredField("logger");
u.putObjectVolatile(cls, u.staticFieldOffset(logger), null);
} catch (Exception e) {
// ignore
}
}
существует еще один вариант, который не требует подавления потока и не зависит от недокументированных или неподдерживаемых API. Используя агент Java, можно переопределить модули для экспорта / открытия необходимых пакетов. Код для этого будет выглядеть примерно так:
void exportAndOpen(Instrumentation instrumentation) {
Set<Module> unnamed =
Collections.singleton(ClassLoader.getSystemClassLoader().getUnnamedModule());
ModuleLayer.boot().modules().forEach(module -> instrumentation.redefineModule(
module,
unnamed,
module.getPackages().stream().collect(Collectors.toMap(
Function.identity(),
pkg -> unnamed
)),
module.getPackages().stream().collect(Collectors.toMap(
Function.identity(),
pkg -> unnamed
)),
Collections.emptySet(),
Collections.emptyMap()
));
}
теперь вы можете запустить любой незаконный доступ без предупреждения, поскольку ваше приложение содержится в неназванном модуле, например:
Method method = ClassLoader.class.getDeclaredMethod("defineClass",
byte[].class, int.class, int.class);
method.setAccessible(true);
для того, чтобы заполучить the Instrumentation
экземпляра, вы можете либо написать Java agent что довольно просто и укажите его в командной строке (а не в пути к классам) с помощью -javaagent:myjar.jar
. Агент будет содержать только premain
метод следующим образом:
public class MyAgent {
public static void main(String arg, Instrumentation inst) {
exportAndOpen(inst);
}
}
кроме того, вы можете динамически подключаться с помощью API-интерфейса attach, который удобно доступен the byte-buddy-agent проект (который я написал):
exportAndOpen(ByteBuddyAgent.install());
что вам нужно позвонить до незаконного доступа. Обратите внимание, что это доступно только на JDKS и на Linux VM, тогда как вам нужно будет предоставить агент Byte Buddy в командной строке в качестве агента Java, если вам это нужно на других виртуальных машинах. Это может быть удобно, если вы хотите, чтобы самонакрепление на тестовых и развивающих машинах, где обычно устанавливаются JDKs.
как указывали другие, это должно служить только промежуточным решением, но я полностью понимаю, что текущее поведение часто ломается ведение журнала искателей и консольных приложений, поэтому я сам использовал это в производственных средах в качестве краткосрочного решения для использования Java 9 и так долго я не сталкивался с проблемами.
хорошо, однако, что это решение является надежным для будущих обновлений, поскольку любая операция, даже динамическое вложение является законным. Используя вспомогательный процесс, Byte Buddy даже работает вокруг обычно запрещенной самоадаптации.
Я не знаю, как достичь того, о чем вы просите. Как вы указали, вам нужно будет добавить опции командной строки (--add-opens
, а не --illegal-access=deny
) для запуска JVM.
Вы писали:
моя цель-избежать дополнительных инструкций для конечных пользователей. У нас много пользователей с установленными серверами, и это было бы большим неудобством для них.
взглядами его, ваши требования только выходят вывод о том, что проект не готов к Java 9. Он должен честно сообщить своим пользователям, что требуется немного больше времени, чтобы быть полностью совместимым с Java 9. Это совершенно нормально в начале после релиза.
вы можете open
пакетов module-info.java
или создать open module
.
например: проверка Шаг 5 и 6 из миграция вашего проекта в Jigsaw шаг за шагом
module shedlock.example {
requires spring.context;
requires spring.jdbc;
requires slf4j.api;
requires shedlock.core;
requires shedlock.spring;
requires HikariCP;
requires shedlock.provider.jdbc.template;
requires java.sql;
opens net.javacrumbs.shedlockexample to spring.core, spring.beans, spring.context;
}
open module shedlock.example {
requires spring.context;
requires spring.jdbc;
requires slf4j.api;
requires shedlock.core;
requires shedlock.spring;
requires HikariCP;
requires shedlock.provider.jdbc.template;
requires java.sql;
}