Как скрыть предупреждение "незаконный отражающий доступ" в 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;
}